Fix UnsupportedOperationException in readTrailingHeaders#16412
Fix UnsupportedOperationException in readTrailingHeaders#16412normanmaurer merged 1 commit intonetty:4.2from
Conversation
|
Great catch! |
|
Thanks! Happy to contribute :) |
|
@vietj as well... PTAL |
yawkat
left a comment
There was a problem hiding this comment.
Looks fine in principle, but this is dangerous code to touch… It's a niche feature, it relates to HTTP/1.1 parsing, the patch expands the permitted inputs. That's the perfect mix for a new request smuggling vuln
codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java
Outdated
Show resolved
Hide resolved
codec-http/src/test/java/io/netty/handler/codec/http/HttpRequestDecoderTest.java
Show resolved
Hide resolved
codec-http/src/test/java/io/netty/handler/codec/http/HttpResponseDecoderTest.java
Show resolved
Hide resolved
codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java
Outdated
Show resolved
Hide resolved
|
Hey @yawkat, thanks for reviewing the PR. I agree it is a niche feature and I'm not sure anyone is actively using trailers with obs-folding in practice. Regarding your comment about expanding permitted inputs — could you clarify what you mean? The goal of this fix is not to widen the accepted input space, but to correctly implement what the original code already intended to support. The previous logic explicitly handled obs-folding in trailers, it just crashed with an UnsupportedOperationException due to calling List.set() on an AbstractList that does not override it. So the permitted inputs are unchanged in intent — the fix just makes the implementation match that intent. |
Motivation: When a chunked HTTP message contains a trailer field with a folded value (obs-fold continuation line starting with SP or HT), readTrailingHeaders threw UnsupportedOperationException. This happened because the folding logic called List.set() on the list returned by trailingHeaders().getAll(), which is an AbstractList implementation that does not override set(). Modifications: Changed readTrailingHeaders in HttpObjectDecoder to accumulate folded continuation lines into the instance-level `value` field, mirroring the pattern already used by readHeaders. The previous header is now flushed via trailingHeaders().add() only once its complete value is assembled, eliminating the need to mutate the list returned by getAll(). Forbidden trailer fields (Content-Length, Transfer-Encoding, Trailer) are filtered at flush time, consistent with the previous behaviour. Added tests to HttpRequestDecoderTest and HttpResponseDecoderTest covering: - A single trailer field - A folded trailer field with multiple SP and HT continuation lines, followed by a non-folded trailer to verify isolation between fields - Forbidden trailer fields interleaved with a valid one, with a forbidden field placed last to exercise the post-loop flush path Result: Chunked HTTP messages with folded trailer values are now decoded correctly instead of throwing UnsupportedOperationException.
codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java
Dismissed
Show dismissed
Hide dismissed
codec-http/src/main/java/io/netty/handler/codec/http/HttpObjectDecoder.java
Dismissed
Show dismissed
Hide dismissed
|
I understand the previous code was buggy, but that behavior may have protected us against more exotic request smuggling. Fixing it essentially does broaden the scope of accepted inputs. Not saying the code should be left as-is, but it is dangerous to modify |
I think we should merge this... @furkanvarol did you sign our ICLA yet ? https://netty.io/s/icla |
|
@yawkat Understood — though if we decide obs-folding in trailers should not be supported, that should be an explicit rejection (e.g. a decode error) rather than relying on an accidental crash as the enforcement mechanism. Happy to go that route if preferred. @normanmaurer yes, I already signed it :) |
|
Could not create auto-port PR. |
Motivation: When a chunked HTTP message contains a trailer field with a folded value (obs-fold continuation line starting with SP or HT), readTrailingHeaders threw UnsupportedOperationException. This happened because the folding logic called List.set() on the list returned by trailingHeaders().getAll(), which is an AbstractList implementation that does not override set(). Modifications: Changed readTrailingHeaders in HttpObjectDecoder to accumulate folded continuation lines into the instance-level `value` field, mirroring the pattern already used by readHeaders. The previous header is now flushed via trailingHeaders().add() only once its complete value is assembled, eliminating the need to mutate the list returned by getAll(). Forbidden trailer fields (Content-Length, Transfer-Encoding, Trailer) are filtered at flush time, consistent with the previous behaviour. Added tests to HttpRequestDecoderTest and HttpResponseDecoderTest covering: - A single trailer field - A folded trailer field with multiple SP and HT continuation lines, followed by a non-folded trailer to verify isolation between fields - Forbidden trailer fields interleaved with a valid one, with a forbidden field placed last to exercise the post-loop flush path Result: Chunked HTTP messages with folded trailer values are now decoded correctly instead of throwing UnsupportedOperationException. (cherry picked from commit 9a85f9f)
|
Auto-port PR for 4.1: #16437 |
…rs (#16437) Auto-port of #16412 to 4.1 Cherry-picked commit: 9a85f9f --- Motivation: When a chunked HTTP message contains a trailer field with a folded value (obs-fold continuation line starting with SP or HT), readTrailingHeaders threw UnsupportedOperationException. This happened because the folding logic called List.set() on the list returned by trailingHeaders().getAll(), which is an AbstractList implementation that does not override set(). Modifications: Changed readTrailingHeaders in HttpObjectDecoder to accumulate folded continuation lines into the instance-level `value` field, mirroring the pattern already used by readHeaders. The previous header is now flushed via trailingHeaders().add() only once its complete value is assembled, eliminating the need to mutate the list returned by getAll(). Forbidden trailer fields (Content-Length, Transfer-Encoding, Trailer) are filtered at flush time, consistent with the previous behaviour. Added tests to HttpRequestDecoderTest and HttpResponseDecoderTest covering: - A single trailer field - A folded trailer field with multiple SP and HT continuation lines, followed by a non-folded trailer to verify isolation between fields - Forbidden trailer fields interleaved with a valid one, with a forbidden field placed last to exercise the post-loop flush path Result: Chunked HTTP messages with folded trailer values are now decoded correctly instead of throwing UnsupportedOperationException. Co-authored-by: Furkan Varol <furkanvarol@users.noreply.github.com>
Motivation: When a chunked HTTP message contains a trailer field with a folded value (obs-fold continuation line starting with SP or HT), readTrailingHeaders threw UnsupportedOperationException. This happened because the folding logic called List.set() on the list returned by trailingHeaders().getAll(), which is an AbstractList implementation that does not override set(). Modifications: Changed readTrailingHeaders in HttpObjectDecoder to accumulate folded continuation lines into the instance-level `value` field, mirroring the pattern already used by readHeaders. The previous header is now flushed via trailingHeaders().add() only once its complete value is assembled, eliminating the need to mutate the list returned by getAll(). Forbidden trailer fields (Content-Length, Transfer-Encoding, Trailer) are filtered at flush time, consistent with the previous behaviour. Added tests to HttpRequestDecoderTest and HttpResponseDecoderTest covering: - A single trailer field - A folded trailer field with multiple SP and HT continuation lines, followed by a non-folded trailer to verify isolation between fields - Forbidden trailer fields interleaved with a valid one, with a forbidden field placed last to exercise the post-loop flush path Result: Chunked HTTP messages with folded trailer values are now decoded correctly instead of throwing UnsupportedOperationException. (cherry picked from commit 9a85f9f)
|
Port to 5.0: #16453 |
…6453) Motivation: When a chunked HTTP message contains a trailer field with a folded value (obs-fold continuation line starting with SP or HT), readTrailingHeaders threw UnsupportedOperationException. This happened because the folding logic called List.set() on the list returned by trailingHeaders().getAll(), which is an AbstractList implementation that does not override set(). Modifications: Changed readTrailingHeaders in HttpObjectDecoder to accumulate folded continuation lines into the instance-level `value` field, mirroring the pattern already used by readHeaders. The previous header is now flushed via trailingHeaders().add() only once its complete value is assembled, eliminating the need to mutate the list returned by getAll(). Forbidden trailer fields (Content-Length, Transfer-Encoding, Trailer) are filtered at flush time, consistent with the previous behaviour. Added tests to HttpRequestDecoderTest and HttpResponseDecoderTest covering: - A single trailer field - A folded trailer field with multiple SP and HT continuation lines, followed by a non-folded trailer to verify isolation between fields - Forbidden trailer fields interleaved with a valid one, with a forbidden field placed last to exercise the post-loop flush path Result: Chunked HTTP messages with folded trailer values are now decoded correctly instead of throwing UnsupportedOperationException. (cherry picked from commit 9a85f9f) Co-authored-by: Furkan Varol <furkanvarol@users.noreply.github.com>
### What changes were proposed in this pull request? This PR upgrades `Netty` to 4.2.12.Final. ### Why are the changes needed? To bring in the latest bug fixes. - https://netty.io/news/2026/03/24/4-2-12-Final.html - netty/netty#16550 - https://netty.io/news/2026/03/24/4-2-11-Final.html - netty/netty#16489 - netty/netty#16536 - netty/netty#16412 ### Does this PR introduce _any_ user-facing change? No. ### How was this patch tested? CI should pass with the existing tests. ### Was this patch authored or co-authored using generative AI tooling? Generated-by: Claude Code (Claude Opus 4.6) Closes #589 from dongjoon-hyun/SPARK-56213. Authored-by: Dongjoon Hyun <dongjoon@apache.org> Signed-off-by: Dongjoon Hyun <dongjoon@apache.org>
### What changes were proposed in this pull request? This PR upgrades `Netty` to 4.2.12.Final. ### Why are the changes needed? To bring in the latest bug fixes. - https://netty.io/news/2026/03/24/4-2-12-Final.html - netty/netty#16550 - https://netty.io/news/2026/03/24/4-2-11-Final.html - netty/netty#16489 - netty/netty#16536 - netty/netty#16412 ### Does this PR introduce _any_ user-facing change? No. ### How was this patch tested? CI should pass with the existing tests. ### Was this patch authored or co-authored using generative AI tooling? Generated-by: Claude Code (Claude Opus 4.6) Closes #55016 from dongjoon-hyun/SPARK-56214. Authored-by: Dongjoon Hyun <dongjoon@apache.org> Signed-off-by: Dongjoon Hyun <dongjoon@apache.org>
Motivation:
When a chunked HTTP message contains a trailer field with a folded value (obs-fold continuation line starting with SP or HT), readTrailingHeaders threw UnsupportedOperationException. This happened because the folding logic called List.set() on the list returned by trailingHeaders().getAll(), which is an AbstractList implementation that does not override set().
Modifications:
Changed readTrailingHeaders in HttpObjectDecoder to accumulate folded continuation lines into the instance-level
valuefield, mirroring the pattern already used by readHeaders. The previous header is now flushed via trailingHeaders().add() only once its complete value is assembled, eliminating the need to mutate the list returned by getAll(). Forbidden trailer fields (Content-Length, Transfer-Encoding, Trailer) are filtered at flush time, consistent with the previous behaviour.Added tests to HttpRequestDecoderTest and HttpResponseDecoderTest covering:
Result:
Chunked HTTP messages with folded trailer values are now decoded correctly instead of throwing UnsupportedOperationException.