Most of the documentation updates were already done in gh-36803, this
completes the section with some information on the property path syntax
supported by allowFields/disallowFields.
Closes gh-36789
Prior to this commit, the reconnection attempt count provided to the
reconnect strategy would not give the updated, incremented value but
instead the previous one.
This commit fixes this and ensures the value is incremented before it's
given to the strategy.
Fixes gh-37223
Prior to this commit, the `DefaultClientResponseBuilder` would assume
that an original client HTTP response, when mutated, would not be reused
nor read anymore. While this is the advised use case, there was some
inconsistency with the builder API here when mutating: some data like
the response status would copied, but the HTTP headers and cookies would
refer directly to the previous entries, making all changes visible to
the previous response instance.
This commit ensures that deep copies are performed when mutating a
client response with the builder API.
Fixes gh-37086
FlashMap.startExpirationPeriod(int) and MockMvcWebConnection's cookie
handling both multiplied an int number of seconds by 1000 without
widening to long. Above 2_147_483 seconds (about 24.9 days) the
multiplication overflows to a negative offset, so the computed
expiration time lands in the past.
For FlashMap, a flash map configured through
AbstractFlashMapManager.setFlashMapTimeout(int) with a large timeout is
then treated as expired immediately. For MockMvcWebConnection, a cookie
with a large max-age is removed from the CookieManager instead of being
stored.
This applies the same widening already used for this pattern in
gh-25613.
Closes gh-37208
Signed-off-by: kogun <akogun@gmail.com>
Prior to this commit, the `DefaultApiVersionStrategy` reactive variant
would attempt to resolve the API version with the first resolver that
replies with a version. This contradicts the API that registers
resolvers in order.
This commit ensures that each resolver is called in order.
Signed-off-by: Gimin Kim <138752849+Gimini-3@users.noreply.github.com>
BodyState.flush() emits every queued buffer and only clears the queue
afterwards, so a cancellation arriving while it emits makes dispose()
release buffers whose ownership has already been transferred to the sink.
Such a buffer is then released twice: once by the parser, and once by the
downstream consumer or the discard hook. With Netty, body buffers are
slices of the inbound buffer, so the second release frees the inbound
buffer prematurely, which surfaces as
IllegalReferenceCountException: refCnt: 0, decrement: 1
io.netty.handler.codec.http.DefaultHttpContent.release
reactor.netty.channel.FluxReceive.drainReceiver
when reactor-netty releases its own share right after onNext.
Remove each buffer from the queue before emitting it, mirroring what
enqueue() already does, so that dispose() only ever releases buffers the
parser still owns.
Signed-off-by: Hyunsik Kang <cj848@hanmail.net>
When a multipart subscriber cancels while MultipartParser has already
emitted body tokens beyond the downstream demand, those tokens are held
in the Flux.create sink queue (and in downstream operator queues such
as windowUntil). On cancellation, Reactor discards the queued tokens,
but BodyToken is not a DataBuffer, so the buffers inside the discarded
tokens are never released and Netty reports "LEAK: ByteBuf.release()
was not called before it's garbage-collected".
Register a doOnDiscard hook for BodyToken in MultipartParser.parse() so
that a discarded body token releases its buffer, both in the sink queue
and in any downstream operator queue that supports discarding.
Closes gh-37115
Signed-off-by: Hyunsik Kang <cj848@hanmail.net>
Prior to this commit,
`MockHttpServletRequest.isRequestedSessionIdValid()` would return `true`
by default and could only be changed manually with a setter. This does
not align with the Servlet spec because of 1) its default value and 2)
it does not react to `changeSessionId()` calls.
This commit fixes that behavior while still allowing "manual" booleans
being set here.
Fixes gh-36631
The builder method required a non-null contextPath while the underlying
field, MutatedServerHttpRequest constructor, and RequestPath.parse all
accept null and treat it the same as an empty string. Relax the method
parameter to @Nullable so callers can clear the context path directly.
Closes gh-37099
Signed-off-by: Garvit Joshi <garvitjoshi9@gmail.com>
Prior to this commit, SpelParserConfiguration exposed 8 overloaded
constructors that accumulated over time as new configuration options
were introduced (auto-grow support since 3.0, maximumExpressionLength
in 5.2.25, maximumOperations in 6.2.19, and maximumBigPowerBits in
7.0.9), culminating in an 8-parameter constructor. This made call sites
hard to read due to unlabeled sequences of booleans and ints, and it
forced users who wanted to override a single setting to also supply
every other value explicitly.
To address that, this commit introduces a builder API in
SpelParserConfiguration, following the pattern already established by
SimpleEvaluationContext's builder API.
Specifically, SpelParserConfiguration.builder() returns a Builder that
is pre-populated with the same defaults as the no-arg constructor,
including the SpringProperties-driven overrides for the default
compiler mode, maximum operations, and maximum big-power bits -- the
latter two are only resolved lazily in build(), so that overriding them
via the builder never triggers an unnecessary SpringProperties lookup.
Each property has a dedicated, named setter (compilerMode(),
compilerClassLoader(), maximumAutoGrowSize(),
maximumExpressionLength(), maximumOperations(), maximumBigPowerBits()),
and the two auto-grow flags are exposed as simple no-arg opt-ins
(autoGrowNullReferences(), autoGrowCollections()) since they both
default to false. build() delegates to the existing canonical
constructor, so validation and defaults remain centralized in one
place.
In addition, a new SpelParserConfiguration.withDefaults() factory
method has been introduced as shorthand for
SpelParserConfiguration.builder().build(), for the common case where
none of the builder's defaults need to be overridden.
As the one deliberate exception to matching the no-arg constructor's
defaults, the builder defaults maximumAutoGrowSize to 256 -- aligned
with DataBinder.DEFAULT_AUTO_GROW_COLLECTION_LIMIT -- rather than the
constructors' Integer.MAX_VALUE. The constructors keep their legacy
default for backward compatibility, but the builder is a new, opt-in
API that is not bound by that compatibility contract.
This change is purely additive: none of the existing constructors have
been modified or deprecated. Deprecating those constructors in favor of
the builder is being deferred to 7.1, since new deprecations should not
be introduced in a patch release. In the meantime, the Javadoc for the
constructors and for the SPRING_EXPRESSION_*_PROPERTY_NAME constants
has been updated to favor the builder (or a specific Builder setter)
instead of the constructors, and the class-level Javadoc now states
that the constructors are planned to be deprecated in favor of the
builder as of Spring Framework 7.1.
SpelExpressionParser's no-arg constructor, ExpressionState's two
convenience constructors, and StandardBeanExpressionResolver's
ClassLoader-based constructor have all been switched from the
SpelParserConfiguration constructors to the builder (or
withDefaults()). This is behaviorally identical in every case:
autoGrowCollections remains false at each of those call sites, and
maximumAutoGrowSize -- the only property whose default differs between
the constructors and the builder -- has no effect when
autoGrowCollections is false.
Tests have been added in a new SpelParserConfigurationTests class to
verify that the builder's defaults match the no-arg constructor (with
the one intentional maximumAutoGrowSize exception called out above),
that custom values are applied correctly, and that invalid values are
rejected. The nested LegacyConstructorTests class provides regression
coverage for each of the legacy constructors, consolidating their usage
in tests to a single class -- which will keep any future deprecation
warnings confined to this class -- and documents that, unlike the
builder, the canonical constructor does not (yet) reject a negative
maximumAutoGrowSize. The remaining incidental usages of the
SpelParserConfiguration constructors throughout EvaluationTests,
IndexingTests, SpelCompilationCoverageTests, SpelReproTests, and
SpelCompilerTests have been converted to use the builder.
Furthermore, the reference documentation has been updated to recommend
the builder and withDefaults() over the constructors, both in prose and
in the Java/Kotlin examples.
Closes gh-37187
Excluding a path from nohttp scanning has so far required mirroring it
by hand in CheckstyleConventions, in addition to any existing
`.gitignore` entry. However, that extra step is easy to forget, as
happened when the .claude folder was added to `.gitignore` (49d2a202da)
but not to the nohttp excludes (48971139c0), only surfacing later as
an OutOfMemoryError that required a separate heap size increase
(90ad7f947d).
To address that, this commit introduces excludeGitIgnoredPaths() in
CheckstyleConventions, which parses the root `.gitignore` file and
translates its patterns into additional nohttp excludes, so that newly
ignored paths are picked up automatically. Note, however, that the
existing hand-maintained excludes are left in place for entries that
are specific to nohttp and are not otherwise ignored by git.
Closes gh-37164
Prior to this commit, some same-page links in our Antora-based docs
were written using the `xref:` macro, which Antora resolves as a
cross-page reference. However, a same-page target can be misread as a
page reference and break the site build (as nearly happened in
gh-37152), whereas `<<id,text>>` only ever resolves against the current
page's own anchors.
To address that inconsistency and avoid potential future bugs (broken
links), this commit converts all such same-page `xref:` links to
`<<id,text>>`, leaving genuine cross-page `xref:` links are unaffected.
Closes gh-37161
On a developer machine, the nohttp check scans the whole project
directory, and things like local git worktrees can add enough extra
content on disk to push the task past its previous 1g heap limit,
causing an OutOfMemoryError. This goes beyond what was addressed by
excluding the .claude folder from nohttp scanning in 48971139c0.
This commit raises the heap size for checkstyleNohttp specifically
to 1536m, leaving the limit for other Checkstyle tasks unchanged so
as not to increase memory pressure on CI.
Prior to this commit, only the very first AsynchronousFileChannel#write
call in DataBufferUtils$WriteCompletionHandler#hookOnNext(DataBuffer)
was guarded against exceptions escaping synchronously, and even then
only via `catch (RuntimeException ex)`, per the original fix for
gh-36184.
While widening that guard to match the read side's `catch (Throwable
ex)` combined with `Exceptions.throwIfFatal(ex)` (see gh-37143), we
discovered that completed(Integer, Attachment) contains two more direct
`this.channel.write(...)` calls -- for continuing a partial write and
for advancing to the next ByteBuffer within the same DataBuffer's
iterator -- neither of which was guarded at all. Since completed() is
invoked by the channel's own completion callback, typically on a
different thread than the one that issued the original write, a
synchronous exception escaping either of those calls has no path back
to the FluxSink, and the resulting Flux hangs indefinitely, exactly as
described in gh-37143, for any write that receives a partial OS write
or spans multiple ByteBuffers.
To address that, this commit extracts a private write(ByteBuffer, long,
Attachment) helper that wraps the channel.write(...) call with a
try/catch block, routing any non-fatal Throwable -- via
Exceptions.throwIfFatal() -- to the existing failed(Throwable,
Attachment) handler. All three call sites (hookOnNext() and both
branches in completed()) now go through this helper, ensuring the
Flux always terminates with a proper error signal instead of hanging
silently, regardless of which write attempt fails or which thread it
fails on.
See gh-36184
See gh-37143
Closes gh-37145
Prior to this commit, DataBufferUtils$ReadCompletionHandler#read()
invoked AsynchronousFileChannel#read(ByteBuffer, long, Attachment,
CompletionHandler) without guarding against exceptions thrown directly
by that call. Although that method is documented to report failures
asynchronously via the supplied CompletionHandler, some platform-
specific implementations can instead throw synchronously – for
example, on Windows with JDK 25, when the JDK rejects a ByteBuffer
backed by a closeable shared memory Arena, as produced by Netty 4.2's
off-heap buffer allocation.
When such an exception is thrown from a recursive read() invocation
triggered from completed() – which happens once a resource requires
more than a single chunk – the exception has no path back to the
FluxSink: it escapes on whatever thread invoked the CompletionHandler,
and the resulting Flux never signals onError or onComplete. In
practice, this surfaced as an indefinite hang when serving a Resource
whose HTTP response is not a ZeroCopyHttpOutputMessage, since
ResourceHttpMessageWriter falls back to ResourceEncoder, which reads
the resource via DataBufferUtils.
To address that, this commit wraps the channel.read(...) call in a
try/catch block and routes any non-fatal Throwable to the existing
failed(Throwable, Attachment) handler, via Exceptions.throwIfFatal(),
mirroring the equivalent fix already applied to the write side for
gh-36184. This ensures the allocated DataBuffer is released and the
Flux always terminates with a proper error signal instead of leaking a
buffer or hanging silently.
See gh-36184
Closes gh-37143
The limit was previously enforced in XmlEventDecoder, because it is
what parses incoming buffers. However, the actual caching is in
Jaxb2Decoder, which holds on to XML events, but has no good way to
estimate their size.
After this commit XmlEventDecoder no longer enforces memory limits
for async parsing. It releases each buffer immediately anyway.
Instead XmlEventDecoder is only responsible to update the number
of bytes received via a new ReceivedByteTracker type while
Jaxb2XmlDecoder uses the same to perform limit and reset the
count depending on when it is aggregating XML events.
Closes gh-37031
Request predicates support preflight request matching based on the "would be"
request (e.g. target HTTP method) so the actual handler is not meant to be
invoked. That's the case only with a DispatcherHandler setup.
Closes gh-37024
Prior to this commit, SpEL expression compilation could be silently
activated in a SimpleEvaluationContext via the
`spring.expression.compiler.mode` Spring/system property or
SpelParserConfiguration. Once an expression is compiled, the evaluation
guards enforced during interpreted evaluation are no longer applied,
which is at odds with the restricted intent of SimpleEvaluationContext.
To address that, this commit introduces a mechanism analogous to
isAssignmentEnabled() which disables compilation by default in
SimpleEvaluationContext. Specifically:
- A new isCompilationSupported() default method has been introduced in
the EvaluationContext API, which returns true by default.
- SimpleEvaluationContext overrides isCompilationSupported() to return
false by default. However, compilation can be opted into explicitly
via the new withCompilationSupported() method in the
SimpleEvaluationContext.Builder.
- SpelExpression.checkCompile() now consults isCompilationSupported()
before triggering new compilation, ensuring that evaluation within an
EvaluationContext never produces a compiled form of the expression if
the context's isCompilationSupported() method returns false.
- All eight getValue() variants in SpelExpression now consult
isCompilationSupported() before executing a compiled expression,
ensuring that a compiled expression produced via a different
EvaluationContext is not silently reused if the caller inadvertently
switches to an EvaluationContext that does not support compilation.
Closes gh-37035