Prior to this commit, SSE support in Spring would write payloads with
the "data:" prefix (without space). While this is OK with the standard,
this makes it harder for implementations to support reading and writing
payloads with Spring (the round trip use case).
This commit introduces a breaking change and now enforces "data: " in
all variants. This has the potential of breaking some low level test
suites with text/plain or custom media types, but this should overall
make the situation better for developers.
Closes gh-37242
RFC 9651 specifies @«timestamp» as a new format for date headers.
The Deprecation header as specified in RFC 9745, for example, makes use of it.
Make sure this can be parsed using `HttpHeaders#getFirstDate` and `HttpHeaders#getFirstZonedDateTime`
Signed-off-by: Raphael Schweikert <any@sabberworm.com>
The new PartGenerator for parsing multipart requests supports buffering
the content in memory and switching to a file after a configured size.
More specifically, when a multipart part exceeds maxInMemorySize and
InMemoryState switches it over to FileState, the bytes that were
already buffered in memory are flushed to the temp file.
Prior to this commit, this was done via FileState.writeBuffer(), meaning
that the in memory buffered data would not be counted against the
configured limit for writing to a file.
This commit fixes this by writing buffered data with FileState.onBody().
Fixes gh-37238
MIME type parameter names are case-insensitive, and MimeType already
stores them in a LinkedCaseInsensitiveMap. Several code paths, however,
still compared them with case-sensitive String.equals().
As a result, MimeType.hashCode() disagreed with MimeType.equals() for
parameter names that differ only in case, breaking the equals/hashCode
contract: text/plain;FOO=bar and text/plain;foo=bar are equal but hash
differently, so one is not found in a hash-based collection holding the
other. MimeType.compareTo() had the same blind spot for the charset
parameter.
MediaType was affected in two further ways: an out-of-range quality
value escaped validation when spelled Q=, and removeQualityValue() left
a Q= parameter in place.
Signed-off-by: Artyom Tsvirko <36863599+lArtiquel@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>
This commit builds on the previous commit and ensures that
"must-understand" is only used with "no-store". This check is performed
at runtime as a staged interface/builder would be a major breaking
change for a behavior that is highlighted as "SHOULD" in the
specification.
This commit also performs similar runtime checks for:
* cache-public + cache-private
* cache-public + no-store
See gh-36918
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>
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
Prior to this commit, many classes would support writing Server Sent
Events in some way to the response output stream. This has lead to some
code duplication.
This commit refactors the duplicated code in a shared `SseUtils` class.
Closes gh-37065
Prior to this commit, building a "Content-Disposition" header to a
String and then parsing it back would not always result in the original
header.
This commit ensures that ContentDisposition guarantees this and honors
the "equals" contract.
Fixes gh-37064
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
Prior to this commit, many classes would support writing Server Sent
Events in some way to the response output stream. This has lead to some
code duplication.
This commit refactors the duplicated code in a shared `SseUtils` class.
Closes gh-37065
Prior to this commit, building a "Content-Disposition" header to a
String and then parsing it back would not always result in the original
header.
This commit ensures that ContentDisposition guarantees this and honors
the "equals" contract.
Fixes gh-37064
This commit introduces a constructor argument to select whether
to use the standard "Forwarded" header or the "X-Forwarded-*"
alternative headers. A separate property to control support for
X-Forwarded-Prefix.
Closes gh-37090
This commit introduces a constructor argument to select whether
to use the standard "Forwarded" header or the "X-Forwarded-*"
alternative headers. A separate property enables support for
X-Forwarded-Prefix.
Closes gh-37072
This commit adds two new methods in ForwardedHeaderUtils, one to parse
the standard "Forwarded" header only, and another to parse the
"X-Forwarded-*" alternative headers. As those are single parse methods,
a ForwardedInfo container type is necessary to return the results.
See gh-36964
Prior to this commit, gh-36328 avoided using RFC 2047 encoding for the
"filename" parameter and use ISO-8859-1 only. This change unfortunately
caused issues because some implementations might try and detect the
encoding automatically.
This commit restricts the filename parameter to ASCII encoding only by:
* transliterating characters to the closes ASCII character
("é"->"e", "ä"->"ae"...)
* falling back to "_" for other chacacters with non latin alphabet or
emojis
Closes gh-37062
Both encoders acquire a pooled BufferRecycler via
factory._getBufferRecycler() but never return it. Jackson 3 changed the
default pool from a ThreadLocal to a per-factory ConcurrentDequePool that
only refills on an explicit releaseToPool(), so the gap leaves the pool
empty and every encode allocates a fresh recycler and buffers.
Hold the recycler in a local and release it once the generator and byte
builder are done: in encodeValue's finally, and in the streaming path's
doAfterTerminate after generator.close() and byteBuilder.release().
releaseToPool() is idempotent, so sharing the recycler with the generator
cannot double-release.
Closes gh-37059
Signed-off-by: samlightfoot <samueldlightfoot@gmail.com>
The WhatWG URL Standard changed its "domain to ASCII" algorithm when
beStrict is false and the domain is an ASCII string, it now returns
the domain lowercased regardless of Unicode ToASCII's outcome, for web
compatibility. Invalid or ambiguous "xn--" (ACE) labels are no longer
rejected or validated; they are lowercased and accepted, matching
browsers and the web-platform-tests URL cases.
This supersedes the earlier spec revision that only lowercased ASCII
domains whose labels did not start with "xn--". Drop the now-obsolete
"xn--" label detection (which was dead code anyway due to a typo) and
unconditionally lowercase ASCII domains.
Closes gh-37018