Skip to content

Commit

Permalink
Add StreamWriteConstraints (#1056) (for master)
Browse files Browse the repository at this point in the history
  • Loading branch information
pjfanning committed Jul 8, 2023
1 parent 0421de9 commit ab589d8
Show file tree
Hide file tree
Showing 23 changed files with 367 additions and 16 deletions.
13 changes: 13 additions & 0 deletions src/main/java/tools/jackson/core/JsonGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
157 changes: 157 additions & 0 deletions src/main/java/tools/jackson/core/StreamWriteConstraints.java
Original file line number Diff line number Diff line change
@@ -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.
*<p>
* Currently constrained aspects, with default settings, are:
* <ul>
* <li>Maximum Nesting depth: default 1000 (see {@link #DEFAULT_MAX_DEPTH})
* </li>
* </ul>
*
* @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+"()`";
}
}
7 changes: 7 additions & 0 deletions src/main/java/tools/jackson/core/TSFBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,18 @@ public abstract class TSFBuilder<F extends TokenStreamFactory,
*/
protected StreamReadConstraints _streamReadConstraints;

/**
* StreamWriteConstraints to use.
*/
protected StreamWriteConstraints _streamWriteConstraints;

// // // Construction

protected TSFBuilder(StreamReadConstraints src,
StreamWriteConstraints swc,
int formatReadF, int formatWriteF) {
_streamReadConstraints = src;
_streamWriteConstraints = swc;
_factoryFeatures = TokenStreamFactory.DEFAULT_FACTORY_FEATURE_FLAGS;
_streamReadFeatures = TokenStreamFactory.DEFAULT_STREAM_READ_FEATURE_FLAGS;
_streamWriteFeatures = TokenStreamFactory.DEFAULT_STREAM_WRITE_FEATURE_FLAGS;
Expand Down
17 changes: 15 additions & 2 deletions src/main/java/tools/jackson/core/TokenStreamFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,11 @@ public static int collectDefaults() {
*/
protected final StreamReadConstraints _streamReadConstraints;

/**
* Active StreamWriteConstraints to use.
*/
protected final StreamWriteConstraints _streamWriteConstraints;

/*
/**********************************************************************
/* Construction
Expand All @@ -248,8 +253,10 @@ public static int collectDefaults() {
* @param formatWriteFeatures Bitmask of format-specific write features enabled
*/
protected TokenStreamFactory(StreamReadConstraints src,
StreamWriteConstraints swc,
int formatReadFeatures, int formatWriteFeatures) {
_streamReadConstraints = src;
_streamWriteConstraints = swc;
_factoryFeatures = DEFAULT_FACTORY_FEATURE_FLAGS;
_streamReadFeatures = DEFAULT_STREAM_READ_FEATURE_FLAGS;
_streamWriteFeatures = DEFAULT_STREAM_WRITE_FEATURE_FLAGS;
Expand All @@ -271,6 +278,7 @@ protected TokenStreamFactory(StreamReadConstraints src,
protected TokenStreamFactory(TSFBuilder<?,?> baseBuilder)
{
_streamReadConstraints = baseBuilder._streamReadConstraints;
_streamWriteConstraints = baseBuilder._streamWriteConstraints;
_factoryFeatures = baseBuilder.factoryFeaturesMask();
_streamReadFeatures = baseBuilder.streamReadFeaturesMask();
_streamWriteFeatures = baseBuilder.streamWriteFeaturesMask();
Expand All @@ -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;
Expand Down Expand Up @@ -495,6 +504,8 @@ public final int getStreamWriteFeatures() {

public StreamReadConstraints streamReadConstraints() { return _streamReadConstraints; }

public StreamWriteConstraints streamWriteConstraints() { return _streamWriteConstraints; }

/*
/**********************************************************************
/* Factory methods for helper objects
Expand Down Expand Up @@ -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);
}

Expand All @@ -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);
}

Expand Down
3 changes: 2 additions & 1 deletion src/main/java/tools/jackson/core/base/BinaryTSFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

/**
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/tools/jackson/core/base/DecorableTSFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,9 @@ public abstract static class DecorableTSFBuilder<F extends TokenStreamFactory,
// // // Construction

protected DecorableTSFBuilder(StreamReadConstraints src,
StreamWriteConstraints swc,
int formatPF, int formatGF) {
super(src, formatPF, formatGF);
super(src, swc, formatPF, formatGF);
_inputDecorator = null;
_outputDecorator = null;
_generatorDecorators = null;
Expand Down Expand Up @@ -115,8 +116,9 @@ public T addDecorator(JsonGeneratorDecorator dec) {
*/

protected DecorableTSFactory(StreamReadConstraints src,
StreamWriteConstraints swc,
int formatPF, int formatGF) {
super(src, formatPF, formatGF);
super(src, swc, formatPF, formatGF);
_inputDecorator = null;
_outputDecorator = null;
_generatorDecorators = null;
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/tools/jackson/core/base/TextualTSFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ public abstract class TextualTSFactory
*/

protected TextualTSFactory(StreamReadConstraints src,
StreamWriteConstraints swc,
int formatPF, int formatGF) {
super(src, formatPF, formatGF);
super(src, swc, formatPF, formatGF);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ protected TokenFilterContext(int type, TokenFilterContext parent,
super();
_type = type;
_parent = parent;
_nestingDepth = parent == null ? 0 : parent._nestingDepth + 1;
_filter = filter;
_index = -1;
_currentValue = currValue;
Expand Down
21 changes: 18 additions & 3 deletions src/main/java/tools/jackson/core/io/IOContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import tools.jackson.core.JsonEncoding;
import tools.jackson.core.StreamReadConstraints;
import tools.jackson.core.StreamWriteConstraints;
import tools.jackson.core.util.BufferRecycler;
import tools.jackson.core.util.TextBuffer;
import tools.jackson.core.util.ReadConstrainedTextBuffer;
Expand Down Expand Up @@ -54,6 +55,8 @@ public class IOContext

protected final StreamReadConstraints _streamReadConstraints;

protected final StreamWriteConstraints _streamWriteConstraints;

/**
* Reference to the allocated I/O buffer for low-level input reading,
* if any allocated.
Expand Down Expand Up @@ -104,16 +107,21 @@ public class IOContext
* Main constructor to use.
*
* @param streamReadConstraints constraints for streaming reads
* @param streamWriteConstraints constraints for streaming writes
* @param br BufferRecycler to use, if any ({@code null} if none)
* @param contentRef Input source reference for location reporting
* @param managedResource Whether input source is managed (owned) by Jackson library
* @param enc Encoding in use
*/
public IOContext(StreamReadConstraints streamReadConstraints, BufferRecycler br,
ContentReference contentRef, boolean managedResource,
public IOContext(StreamReadConstraints streamReadConstraints,
StreamWriteConstraints streamWriteConstraints,
BufferRecycler br, ContentReference contentRef, boolean managedResource,
JsonEncoding enc)
{
_streamReadConstraints = streamReadConstraints;
_streamReadConstraints = streamReadConstraints == null ?
StreamReadConstraints.defaults() : streamReadConstraints;
_streamWriteConstraints = streamWriteConstraints == null ?
StreamWriteConstraints.defaults() : streamWriteConstraints;;
_bufferRecycler = br;
_contentReference = contentRef;
_managedResource = managedResource;
Expand All @@ -127,6 +135,13 @@ public StreamReadConstraints streamReadConstraints() {
return _streamReadConstraints;
}

/**
* @return constraints for streaming writes
*/
public StreamWriteConstraints streamWriteConstraints() {
return _streamWriteConstraints;
}

public IOContext setEncoding(JsonEncoding enc) {
_encoding = enc;
return this;
Expand Down
4 changes: 2 additions & 2 deletions src/main/java/tools/jackson/core/json/JsonFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ public class JsonFactory
* factory instance.
*/
public JsonFactory() {
super(StreamReadConstraints.defaults(),
super(StreamReadConstraints.defaults(), StreamWriteConstraints.defaults(),
DEFAULT_JSON_PARSER_FEATURE_FLAGS, DEFAULT_JSON_GENERATOR_FEATURE_FLAGS);
_rootValueSeparator = DEFAULT_ROOT_VALUE_SEPARATOR;
_characterEscapes = null;
Expand Down Expand Up @@ -351,7 +351,7 @@ public JsonParser createNonBlockingByteBufferParser(ObjectReadContext readCtxt)
}

protected IOContext _createNonBlockingContext(Object srcRef) {
return new IOContext(_streamReadConstraints, _getBufferRecycler(),
return new IOContext(_streamReadConstraints, _streamWriteConstraints, _getBufferRecycler(),
ContentReference.rawReference(srcRef), false, JsonEncoding.UTF8);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ public class JsonFactoryBuilder extends DecorableTSFBuilder<JsonFactory, JsonFac

public JsonFactoryBuilder() {
super(StreamReadConstraints.defaults(),
StreamWriteConstraints.defaults(),
JsonFactory.DEFAULT_JSON_PARSER_FEATURE_FLAGS,
JsonFactory.DEFAULT_JSON_GENERATOR_FEATURE_FLAGS);
_rootValueSeparator = JsonFactory.DEFAULT_ROOT_VALUE_SEPARATOR;
Expand Down
Loading

0 comments on commit ab589d8

Please sign in to comment.