Skip to content

Commit

Permalink
Add StreamWriteConstraints (#1055)
Browse files Browse the repository at this point in the history
  • Loading branch information
pjfanning authored Jul 8, 2023
1 parent 5fe25f5 commit 833d87b
Show file tree
Hide file tree
Showing 14 changed files with 407 additions and 7 deletions.
49 changes: 46 additions & 3 deletions src/main/java/com/fasterxml/jackson/core/JsonFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,14 @@ public static int collectDefaults() {
*/
protected StreamReadConstraints _streamReadConstraints;

/**
* Write constraints to use for {@link JsonGenerator}s constructed using
* this factory.
*
* @since 2.16
*/
protected StreamWriteConstraints _streamWriteConstraints;

/**
* Optional helper object that may decorate input sources, to do
* additional processing on input during parsing.
Expand Down Expand Up @@ -350,6 +358,7 @@ public JsonFactory(ObjectCodec oc) {
_objectCodec = oc;
_quoteChar = DEFAULT_QUOTE_CHAR;
_streamReadConstraints = StreamReadConstraints.defaults();
_streamWriteConstraints = StreamWriteConstraints.defaults();
_generatorDecorators = null;
}

Expand All @@ -374,6 +383,8 @@ protected JsonFactory(JsonFactory src, ObjectCodec codec)
_generatorDecorators = _copy(src._generatorDecorators);
_streamReadConstraints = src._streamReadConstraints == null ?
StreamReadConstraints.defaults() : src._streamReadConstraints;
_streamWriteConstraints = src._streamWriteConstraints == null ?
StreamWriteConstraints.defaults() : src._streamWriteConstraints;

// JSON-specific
_characterEscapes = src._characterEscapes;
Expand Down Expand Up @@ -401,6 +412,8 @@ public JsonFactory(JsonFactoryBuilder b) {
_generatorDecorators = _copy(b._generatorDecorators);
_streamReadConstraints = b._streamReadConstraints == null ?
StreamReadConstraints.defaults() : b._streamReadConstraints;
_streamWriteConstraints = b._streamWriteConstraints == null ?
StreamWriteConstraints.defaults() : b._streamWriteConstraints;

// JSON-specific
_characterEscapes = b._characterEscapes;
Expand Down Expand Up @@ -428,6 +441,8 @@ protected JsonFactory(TSFBuilder<?,?> b, boolean bogus) {
_generatorDecorators = _copy(b._generatorDecorators);
_streamReadConstraints = b._streamReadConstraints == null ?
StreamReadConstraints.defaults() : b._streamReadConstraints;
_streamWriteConstraints = b._streamWriteConstraints == null ?
StreamWriteConstraints.defaults() : b._streamWriteConstraints;

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

@Override
public StreamWriteConstraints streamWriteConstraints() {
return _streamWriteConstraints;
}

/**
* Method for overriding {@link StreamReadConstraints} defined for
* this factory.
Expand All @@ -822,6 +842,26 @@ public JsonFactory setStreamReadConstraints(StreamReadConstraints src) {
return this;
}

/**
* Method for overriding {@link StreamWriteConstraints} defined for
* this factory.
*<p>
* NOTE: the preferred way to set constraints is by using
* {@link JsonFactoryBuilder#streamWriteConstraints}: this method is only
* provided to support older non-builder-based construction.
* In Jackson 3.x this method will not be available.
*
* @param swc Constraints
*
* @return This factory instance (to allow call chaining)
*
* @since 2.16
*/
public JsonFactory setStreamWriteConstraints(StreamWriteConstraints swc) {
_streamWriteConstraints = Objects.requireNonNull(swc);
return this;
}

/*
/**********************************************************
/* Configuration, parser configuration
Expand Down Expand Up @@ -2076,7 +2116,8 @@ protected IOContext _createContext(ContentReference contentRef, boolean resource
if (contentRef == null) {
contentRef = ContentReference.unknown();
}
return new IOContext(_streamReadConstraints, _getBufferRecycler(), contentRef, resourceManaged);
return new IOContext(_streamReadConstraints, _streamWriteConstraints,
_getBufferRecycler(), contentRef, resourceManaged);
}

/**
Expand All @@ -2091,7 +2132,8 @@ protected IOContext _createContext(ContentReference contentRef, boolean resource
*/
@Deprecated // @since 2.13
protected IOContext _createContext(Object rawContentRef, boolean resourceManaged) {
return new IOContext(_streamReadConstraints, _getBufferRecycler(),
return new IOContext(_streamReadConstraints, _streamWriteConstraints,
_getBufferRecycler(),
_createContentReference(rawContentRef),
resourceManaged);
}
Expand All @@ -2109,7 +2151,8 @@ protected IOContext _createContext(Object rawContentRef, boolean resourceManaged
protected IOContext _createNonBlockingContext(Object srcRef) {
// [jackson-core#479]: allow recycling for non-blocking parser again
// now that access is thread-safe
return new IOContext(_streamReadConstraints, _getBufferRecycler(),
return new IOContext(_streamReadConstraints, _streamWriteConstraints,
_getBufferRecycler(),
_createContentReference(srcRef),
false);
}
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/com/fasterxml/jackson/core/JsonGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -341,6 +341,15 @@ protected JsonGenerator() { }
*/
public abstract ObjectCodec getCodec();

/**
* Get the constraints to apply when performing streaming writes.
*
* @since 2.16
*/
public StreamWriteConstraints streamWriteConstraints() {
return StreamWriteConstraints.defaults();
}

/**
* Accessor for finding out version of the bundle that provided this generator instance.
*
Expand Down
157 changes: 157 additions & 0 deletions src/main/java/com/fasterxml/jackson/core/StreamWriteConstraints.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
package com.fasterxml.jackson.core;

import com.fasterxml.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+"()`";
}
}
21 changes: 20 additions & 1 deletion src/main/java/com/fasterxml/jackson/core/TSFBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -82,12 +82,19 @@ public abstract class TSFBuilder<F extends JsonFactory,
protected OutputDecorator _outputDecorator;

/**
* Optional StreamReadConfig.
* Optional StreamReadConstraints.
*
* @since 2.15
*/
protected StreamReadConstraints _streamReadConstraints;

/**
* Optional StreamWriteConstraints.
*
* @since 2.16
*/
protected StreamWriteConstraints _streamWriteConstraints;

/**
* @since 2.16
*/
Expand Down Expand Up @@ -313,6 +320,18 @@ public B streamReadConstraints(StreamReadConstraints streamReadConstraints) {
return _this();
}

/**
* Sets the constraints for streaming writes.
*
* @param streamWriteConstraints constraints for streaming reads
* @return this factory
* @since 2.16
*/
public B streamWriteConstraints(StreamWriteConstraints streamWriteConstraints) {
_streamWriteConstraints = streamWriteConstraints;
return _this();
}

// // // Other methods

/**
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/com/fasterxml/jackson/core/TokenStreamFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,16 @@ public abstract class TokenStreamFactory
*/
public abstract StreamReadConstraints streamReadConstraints();

/**
* Get the constraints to apply when performing streaming writes.
*
* @return Constraints to apply to reads done by {@link JsonGenerator}s constructed
* by this factory.
*
* @since 2.16
*/
public abstract StreamWriteConstraints streamWriteConstraints();

/*
/**********************************************************************
/* Factory methods, parsers
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;
_startHandled = startHandled;
Expand Down
Loading

0 comments on commit 833d87b

Please sign in to comment.