Skip to content

Commit 833d87b

Browse files
authored
Add StreamWriteConstraints (#1055)
1 parent 5fe25f5 commit 833d87b

14 files changed

+407
-7
lines changed

src/main/java/com/fasterxml/jackson/core/JsonFactory.java

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -282,6 +282,14 @@ public static int collectDefaults() {
282282
*/
283283
protected StreamReadConstraints _streamReadConstraints;
284284

285+
/**
286+
* Write constraints to use for {@link JsonGenerator}s constructed using
287+
* this factory.
288+
*
289+
* @since 2.16
290+
*/
291+
protected StreamWriteConstraints _streamWriteConstraints;
292+
285293
/**
286294
* Optional helper object that may decorate input sources, to do
287295
* additional processing on input during parsing.
@@ -350,6 +358,7 @@ public JsonFactory(ObjectCodec oc) {
350358
_objectCodec = oc;
351359
_quoteChar = DEFAULT_QUOTE_CHAR;
352360
_streamReadConstraints = StreamReadConstraints.defaults();
361+
_streamWriteConstraints = StreamWriteConstraints.defaults();
353362
_generatorDecorators = null;
354363
}
355364

@@ -374,6 +383,8 @@ protected JsonFactory(JsonFactory src, ObjectCodec codec)
374383
_generatorDecorators = _copy(src._generatorDecorators);
375384
_streamReadConstraints = src._streamReadConstraints == null ?
376385
StreamReadConstraints.defaults() : src._streamReadConstraints;
386+
_streamWriteConstraints = src._streamWriteConstraints == null ?
387+
StreamWriteConstraints.defaults() : src._streamWriteConstraints;
377388

378389
// JSON-specific
379390
_characterEscapes = src._characterEscapes;
@@ -401,6 +412,8 @@ public JsonFactory(JsonFactoryBuilder b) {
401412
_generatorDecorators = _copy(b._generatorDecorators);
402413
_streamReadConstraints = b._streamReadConstraints == null ?
403414
StreamReadConstraints.defaults() : b._streamReadConstraints;
415+
_streamWriteConstraints = b._streamWriteConstraints == null ?
416+
StreamWriteConstraints.defaults() : b._streamWriteConstraints;
404417

405418
// JSON-specific
406419
_characterEscapes = b._characterEscapes;
@@ -428,6 +441,8 @@ protected JsonFactory(TSFBuilder<?,?> b, boolean bogus) {
428441
_generatorDecorators = _copy(b._generatorDecorators);
429442
_streamReadConstraints = b._streamReadConstraints == null ?
430443
StreamReadConstraints.defaults() : b._streamReadConstraints;
444+
_streamWriteConstraints = b._streamWriteConstraints == null ?
445+
StreamWriteConstraints.defaults() : b._streamWriteConstraints;
431446

432447
// JSON-specific: need to assign even if not really used
433448
_characterEscapes = null;
@@ -802,6 +817,11 @@ public StreamReadConstraints streamReadConstraints() {
802817
return _streamReadConstraints;
803818
}
804819

820+
@Override
821+
public StreamWriteConstraints streamWriteConstraints() {
822+
return _streamWriteConstraints;
823+
}
824+
805825
/**
806826
* Method for overriding {@link StreamReadConstraints} defined for
807827
* this factory.
@@ -822,6 +842,26 @@ public JsonFactory setStreamReadConstraints(StreamReadConstraints src) {
822842
return this;
823843
}
824844

845+
/**
846+
* Method for overriding {@link StreamWriteConstraints} defined for
847+
* this factory.
848+
*<p>
849+
* NOTE: the preferred way to set constraints is by using
850+
* {@link JsonFactoryBuilder#streamWriteConstraints}: this method is only
851+
* provided to support older non-builder-based construction.
852+
* In Jackson 3.x this method will not be available.
853+
*
854+
* @param swc Constraints
855+
*
856+
* @return This factory instance (to allow call chaining)
857+
*
858+
* @since 2.16
859+
*/
860+
public JsonFactory setStreamWriteConstraints(StreamWriteConstraints swc) {
861+
_streamWriteConstraints = Objects.requireNonNull(swc);
862+
return this;
863+
}
864+
825865
/*
826866
/**********************************************************
827867
/* Configuration, parser configuration
@@ -2076,7 +2116,8 @@ protected IOContext _createContext(ContentReference contentRef, boolean resource
20762116
if (contentRef == null) {
20772117
contentRef = ContentReference.unknown();
20782118
}
2079-
return new IOContext(_streamReadConstraints, _getBufferRecycler(), contentRef, resourceManaged);
2119+
return new IOContext(_streamReadConstraints, _streamWriteConstraints,
2120+
_getBufferRecycler(), contentRef, resourceManaged);
20802121
}
20812122

20822123
/**
@@ -2091,7 +2132,8 @@ protected IOContext _createContext(ContentReference contentRef, boolean resource
20912132
*/
20922133
@Deprecated // @since 2.13
20932134
protected IOContext _createContext(Object rawContentRef, boolean resourceManaged) {
2094-
return new IOContext(_streamReadConstraints, _getBufferRecycler(),
2135+
return new IOContext(_streamReadConstraints, _streamWriteConstraints,
2136+
_getBufferRecycler(),
20952137
_createContentReference(rawContentRef),
20962138
resourceManaged);
20972139
}
@@ -2109,7 +2151,8 @@ protected IOContext _createContext(Object rawContentRef, boolean resourceManaged
21092151
protected IOContext _createNonBlockingContext(Object srcRef) {
21102152
// [jackson-core#479]: allow recycling for non-blocking parser again
21112153
// now that access is thread-safe
2112-
return new IOContext(_streamReadConstraints, _getBufferRecycler(),
2154+
return new IOContext(_streamReadConstraints, _streamWriteConstraints,
2155+
_getBufferRecycler(),
21132156
_createContentReference(srcRef),
21142157
false);
21152158
}

src/main/java/com/fasterxml/jackson/core/JsonGenerator.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -341,6 +341,15 @@ protected JsonGenerator() { }
341341
*/
342342
public abstract ObjectCodec getCodec();
343343

344+
/**
345+
* Get the constraints to apply when performing streaming writes.
346+
*
347+
* @since 2.16
348+
*/
349+
public StreamWriteConstraints streamWriteConstraints() {
350+
return StreamWriteConstraints.defaults();
351+
}
352+
344353
/**
345354
* Accessor for finding out version of the bundle that provided this generator instance.
346355
*
Lines changed: 157 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,157 @@
1+
package com.fasterxml.jackson.core;
2+
3+
import com.fasterxml.jackson.core.exc.StreamConstraintsException;
4+
5+
/**
6+
* The constraints to use for streaming writes: used to guard against problematic
7+
* output by preventing processing of "too big" output constructs (values,
8+
* structures).
9+
* Constraints are registered with {@code TokenStreamFactory} (such as
10+
* {@code JsonFactory}); if nothing explicitly specified, default
11+
* constraints are used.
12+
*<p>
13+
* Currently constrained aspects, with default settings, are:
14+
* <ul>
15+
* <li>Maximum Nesting depth: default 1000 (see {@link #DEFAULT_MAX_DEPTH})
16+
* </li>
17+
* </ul>
18+
*
19+
* @since 2.16
20+
*/
21+
public class StreamWriteConstraints
22+
implements java.io.Serializable
23+
{
24+
private static final long serialVersionUID = 1L;
25+
26+
/**
27+
* Default setting for maximum depth: see {@link Builder#maxNestingDepth(int)} for details.
28+
*/
29+
public static final int DEFAULT_MAX_DEPTH = 1000;
30+
31+
protected final int _maxNestingDepth;
32+
33+
private static final StreamWriteConstraints DEFAULT =
34+
new StreamWriteConstraints(DEFAULT_MAX_DEPTH);
35+
36+
public static final class Builder {
37+
private int maxNestingDepth;
38+
39+
/**
40+
* Sets the maximum nesting depth. The depth is a count of objects and arrays that have not
41+
* been closed, `{` and `[` respectively.
42+
*
43+
* @param maxNestingDepth the maximum depth
44+
*
45+
* @return this builder
46+
* @throws IllegalArgumentException if the maxNestingDepth is set to a negative value
47+
*/
48+
public Builder maxNestingDepth(final int maxNestingDepth) {
49+
if (maxNestingDepth < 0) {
50+
throw new IllegalArgumentException("Cannot set maxNestingDepth to a negative value");
51+
}
52+
this.maxNestingDepth = maxNestingDepth;
53+
return this;
54+
}
55+
56+
Builder() {
57+
this(DEFAULT_MAX_DEPTH);
58+
}
59+
60+
Builder(final int maxNestingDepth) {
61+
this.maxNestingDepth = maxNestingDepth;
62+
}
63+
64+
Builder(StreamWriteConstraints src) {
65+
maxNestingDepth = src._maxNestingDepth;
66+
}
67+
68+
public StreamWriteConstraints build() {
69+
return new StreamWriteConstraints(maxNestingDepth);
70+
}
71+
}
72+
73+
/*
74+
/**********************************************************************
75+
/* Life-cycle
76+
/**********************************************************************
77+
*/
78+
79+
protected StreamWriteConstraints(final int maxNestingDepth) {
80+
_maxNestingDepth = maxNestingDepth;
81+
}
82+
83+
public static Builder builder() {
84+
return new Builder();
85+
}
86+
87+
public static StreamWriteConstraints defaults() {
88+
return DEFAULT;
89+
}
90+
91+
/**
92+
* @return New {@link Builder} initialized with settings of this constraints
93+
* instance
94+
*/
95+
public Builder rebuild() {
96+
return new Builder(this);
97+
}
98+
99+
/*
100+
/**********************************************************************
101+
/* Accessors
102+
/**********************************************************************
103+
*/
104+
105+
/**
106+
* Accessor for maximum depth.
107+
* see {@link Builder#maxNestingDepth(int)} for details.
108+
*
109+
* @return Maximum allowed depth
110+
*/
111+
public int getMaxNestingDepth() {
112+
return _maxNestingDepth;
113+
}
114+
115+
/*
116+
/**********************************************************************
117+
/* Convenience methods for validation, document limits
118+
/**********************************************************************
119+
*/
120+
121+
/**
122+
* Convenience method that can be used to verify that the
123+
* nesting depth does not exceed the maximum specified by this
124+
* constraints object: if it does, a
125+
* {@link StreamConstraintsException}
126+
* is thrown.
127+
*
128+
* @param depth count of unclosed objects and arrays
129+
*
130+
* @throws StreamConstraintsException If depth exceeds maximum
131+
*/
132+
public void validateNestingDepth(int depth) throws StreamConstraintsException
133+
{
134+
if (depth > _maxNestingDepth) {
135+
throw _constructException(
136+
"Document nesting depth (%d) exceeds the maximum allowed (%d, from %s)",
137+
depth, _maxNestingDepth,
138+
_constrainRef("getMaxNestingDepth"));
139+
}
140+
}
141+
142+
/*
143+
/**********************************************************************
144+
/* Error reporting
145+
/**********************************************************************
146+
*/
147+
148+
// @since 2.16
149+
protected StreamConstraintsException _constructException(String msgTemplate, Object... args) throws StreamConstraintsException {
150+
throw new StreamConstraintsException(String.format(msgTemplate, args));
151+
}
152+
153+
// @since 2.16
154+
protected String _constrainRef(String method) {
155+
return "`StreamWriteConstraints."+method+"()`";
156+
}
157+
}

src/main/java/com/fasterxml/jackson/core/TSFBuilder.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,12 +82,19 @@ public abstract class TSFBuilder<F extends JsonFactory,
8282
protected OutputDecorator _outputDecorator;
8383

8484
/**
85-
* Optional StreamReadConfig.
85+
* Optional StreamReadConstraints.
8686
*
8787
* @since 2.15
8888
*/
8989
protected StreamReadConstraints _streamReadConstraints;
9090

91+
/**
92+
* Optional StreamWriteConstraints.
93+
*
94+
* @since 2.16
95+
*/
96+
protected StreamWriteConstraints _streamWriteConstraints;
97+
9198
/**
9299
* @since 2.16
93100
*/
@@ -313,6 +320,18 @@ public B streamReadConstraints(StreamReadConstraints streamReadConstraints) {
313320
return _this();
314321
}
315322

323+
/**
324+
* Sets the constraints for streaming writes.
325+
*
326+
* @param streamWriteConstraints constraints for streaming reads
327+
* @return this factory
328+
* @since 2.16
329+
*/
330+
public B streamWriteConstraints(StreamWriteConstraints streamWriteConstraints) {
331+
_streamWriteConstraints = streamWriteConstraints;
332+
return _this();
333+
}
334+
316335
// // // Other methods
317336

318337
/**

src/main/java/com/fasterxml/jackson/core/TokenStreamFactory.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,16 @@ public abstract class TokenStreamFactory
153153
*/
154154
public abstract StreamReadConstraints streamReadConstraints();
155155

156+
/**
157+
* Get the constraints to apply when performing streaming writes.
158+
*
159+
* @return Constraints to apply to reads done by {@link JsonGenerator}s constructed
160+
* by this factory.
161+
*
162+
* @since 2.16
163+
*/
164+
public abstract StreamWriteConstraints streamWriteConstraints();
165+
156166
/*
157167
/**********************************************************************
158168
/* Factory methods, parsers

src/main/java/com/fasterxml/jackson/core/filter/TokenFilterContext.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ protected TokenFilterContext(int type, TokenFilterContext parent,
7171
super();
7272
_type = type;
7373
_parent = parent;
74+
_nestingDepth = parent == null ? 0 : parent._nestingDepth + 1;
7475
_filter = filter;
7576
_index = -1;
7677
_startHandled = startHandled;

0 commit comments

Comments
 (0)