diff --git a/src/main/java/tools/jackson/core/JsonGenerator.java b/src/main/java/tools/jackson/core/JsonGenerator.java
index 6409483e08..c107a954f8 100644
--- a/src/main/java/tools/jackson/core/JsonGenerator.java
+++ b/src/main/java/tools/jackson/core/JsonGenerator.java
@@ -50,6 +50,19 @@ protected JsonGenerator() { }
@Override
public abstract Version version();
+ /*
+ /**********************************************************************
+ /* Constraints violation checking
+ /**********************************************************************
+ */
+
+ /**
+ * Get the constraints to apply when performing streaming writes.
+ */
+ public StreamWriteConstraints streamWriteConstraints() {
+ return StreamWriteConstraints.defaults();
+ }
+
/*
/**********************************************************************
/* Public API, output configuration, state access
diff --git a/src/main/java/tools/jackson/core/StreamWriteConstraints.java b/src/main/java/tools/jackson/core/StreamWriteConstraints.java
new file mode 100644
index 0000000000..cb25f32ac4
--- /dev/null
+++ b/src/main/java/tools/jackson/core/StreamWriteConstraints.java
@@ -0,0 +1,157 @@
+package tools.jackson.core;
+
+import tools.jackson.core.exc.StreamConstraintsException;
+
+/**
+ * The constraints to use for streaming writes: used to guard against problematic
+ * output by preventing processing of "too big" output constructs (values,
+ * structures).
+ * Constraints are registered with {@code TokenStreamFactory} (such as
+ * {@code JsonFactory}); if nothing explicitly specified, default
+ * constraints are used.
+ *
+ * Currently constrained aspects, with default settings, are:
+ *
+ * - Maximum Nesting depth: default 1000 (see {@link #DEFAULT_MAX_DEPTH})
+ *
+ *
+ *
+ * @since 2.16
+ */
+public class StreamWriteConstraints
+ implements java.io.Serializable
+{
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Default setting for maximum depth: see {@link Builder#maxNestingDepth(int)} for details.
+ */
+ public static final int DEFAULT_MAX_DEPTH = 1000;
+
+ protected final int _maxNestingDepth;
+
+ private static final StreamWriteConstraints DEFAULT =
+ new StreamWriteConstraints(DEFAULT_MAX_DEPTH);
+
+ public static final class Builder {
+ private int maxNestingDepth;
+
+ /**
+ * Sets the maximum nesting depth. The depth is a count of objects and arrays that have not
+ * been closed, `{` and `[` respectively.
+ *
+ * @param maxNestingDepth the maximum depth
+ *
+ * @return this builder
+ * @throws IllegalArgumentException if the maxNestingDepth is set to a negative value
+ */
+ public Builder maxNestingDepth(final int maxNestingDepth) {
+ if (maxNestingDepth < 0) {
+ throw new IllegalArgumentException("Cannot set maxNestingDepth to a negative value");
+ }
+ this.maxNestingDepth = maxNestingDepth;
+ return this;
+ }
+
+ Builder() {
+ this(DEFAULT_MAX_DEPTH);
+ }
+
+ Builder(final int maxNestingDepth) {
+ this.maxNestingDepth = maxNestingDepth;
+ }
+
+ Builder(StreamWriteConstraints src) {
+ maxNestingDepth = src._maxNestingDepth;
+ }
+
+ public StreamWriteConstraints build() {
+ return new StreamWriteConstraints(maxNestingDepth);
+ }
+ }
+
+ /*
+ /**********************************************************************
+ /* Life-cycle
+ /**********************************************************************
+ */
+
+ protected StreamWriteConstraints(final int maxNestingDepth) {
+ _maxNestingDepth = maxNestingDepth;
+ }
+
+ public static Builder builder() {
+ return new Builder();
+ }
+
+ public static StreamWriteConstraints defaults() {
+ return DEFAULT;
+ }
+
+ /**
+ * @return New {@link Builder} initialized with settings of this constraints
+ * instance
+ */
+ public Builder rebuild() {
+ return new Builder(this);
+ }
+
+ /*
+ /**********************************************************************
+ /* Accessors
+ /**********************************************************************
+ */
+
+ /**
+ * Accessor for maximum depth.
+ * see {@link Builder#maxNestingDepth(int)} for details.
+ *
+ * @return Maximum allowed depth
+ */
+ public int getMaxNestingDepth() {
+ return _maxNestingDepth;
+ }
+
+ /*
+ /**********************************************************************
+ /* Convenience methods for validation, document limits
+ /**********************************************************************
+ */
+
+ /**
+ * Convenience method that can be used to verify that the
+ * nesting depth does not exceed the maximum specified by this
+ * constraints object: if it does, a
+ * {@link StreamConstraintsException}
+ * is thrown.
+ *
+ * @param depth count of unclosed objects and arrays
+ *
+ * @throws StreamConstraintsException If depth exceeds maximum
+ */
+ public void validateNestingDepth(int depth) throws StreamConstraintsException
+ {
+ if (depth > _maxNestingDepth) {
+ throw _constructException(
+ "Document nesting depth (%d) exceeds the maximum allowed (%d, from %s)",
+ depth, _maxNestingDepth,
+ _constrainRef("getMaxNestingDepth"));
+ }
+ }
+
+ /*
+ /**********************************************************************
+ /* Error reporting
+ /**********************************************************************
+ */
+
+ // @since 2.16
+ protected StreamConstraintsException _constructException(String msgTemplate, Object... args) throws StreamConstraintsException {
+ throw new StreamConstraintsException(String.format(msgTemplate, args));
+ }
+
+ // @since 2.16
+ protected String _constrainRef(String method) {
+ return "`StreamWriteConstraints."+method+"()`";
+ }
+}
diff --git a/src/main/java/tools/jackson/core/TSFBuilder.java b/src/main/java/tools/jackson/core/TSFBuilder.java
index 19dc9b61d6..56cf460d71 100644
--- a/src/main/java/tools/jackson/core/TSFBuilder.java
+++ b/src/main/java/tools/jackson/core/TSFBuilder.java
@@ -37,11 +37,18 @@ public abstract class TSFBuilder baseBuilder)
{
_streamReadConstraints = baseBuilder._streamReadConstraints;
+ _streamWriteConstraints = baseBuilder._streamWriteConstraints;
_factoryFeatures = baseBuilder.factoryFeaturesMask();
_streamReadFeatures = baseBuilder.streamReadFeaturesMask();
_streamWriteFeatures = baseBuilder.streamWriteFeaturesMask();
@@ -287,6 +295,7 @@ protected TokenStreamFactory(TSFBuilder,?> baseBuilder)
protected TokenStreamFactory(TokenStreamFactory src)
{
_streamReadConstraints = src._streamReadConstraints;
+ _streamWriteConstraints = src._streamWriteConstraints;
_factoryFeatures = src._factoryFeatures;
_streamReadFeatures = src._streamReadFeatures;
_streamWriteFeatures = src._streamWriteFeatures;
@@ -495,6 +504,8 @@ public final int getStreamWriteFeatures() {
public StreamReadConstraints streamReadConstraints() { return _streamReadConstraints; }
+ public StreamWriteConstraints streamWriteConstraints() { return _streamWriteConstraints; }
+
/*
/**********************************************************************
/* Factory methods for helper objects
@@ -1200,7 +1211,8 @@ public BufferRecycler _getBufferRecycler()
* @return Context constructed
*/
protected IOContext _createContext(ContentReference contentRef, boolean resourceManaged) {
- return new IOContext(_streamReadConstraints, _getBufferRecycler(), contentRef,
+ return new IOContext(_streamReadConstraints, _streamWriteConstraints,
+ _getBufferRecycler(), contentRef,
resourceManaged, null);
}
@@ -1216,7 +1228,8 @@ protected IOContext _createContext(ContentReference contentRef, boolean resource
*/
protected IOContext _createContext(ContentReference contentRef, boolean resourceManaged,
JsonEncoding enc) {
- return new IOContext(_streamReadConstraints, _getBufferRecycler(), contentRef,
+ return new IOContext(_streamReadConstraints, _streamWriteConstraints,
+ _getBufferRecycler(), contentRef,
resourceManaged, enc);
}
diff --git a/src/main/java/tools/jackson/core/base/BinaryTSFactory.java b/src/main/java/tools/jackson/core/base/BinaryTSFactory.java
index 0ab9bb40a6..cb820bdddd 100644
--- a/src/main/java/tools/jackson/core/base/BinaryTSFactory.java
+++ b/src/main/java/tools/jackson/core/base/BinaryTSFactory.java
@@ -26,8 +26,9 @@ public abstract class BinaryTSFactory
*/
protected BinaryTSFactory(StreamReadConstraints src,
+ StreamWriteConstraints swc,
int formatPF, int formatGF) {
- super(src, formatPF, formatGF);
+ super(src, swc, formatPF, formatGF);
}
/**
diff --git a/src/main/java/tools/jackson/core/base/DecorableTSFactory.java b/src/main/java/tools/jackson/core/base/DecorableTSFactory.java
index 69eb19a4a7..58b9ea9654 100644
--- a/src/main/java/tools/jackson/core/base/DecorableTSFactory.java
+++ b/src/main/java/tools/jackson/core/base/DecorableTSFactory.java
@@ -46,8 +46,9 @@ public abstract static class DecorableTSFBuilder