allthingssecurity opened a new pull request, #28758:
URL: https://github.com/apache/camel/pull/28758

   # Description
   
   [CAMEL-26090](https://issues.apache.org/jira/browse/CAMEL-26090)
   
   The Netty decoder of camel-hl7, `HL7MLLPNettyDecoder` (made by 
`HL7MLLPNettyDecoderFactory`), is a Netty `DelimiterBasedFrameDecoder` whose 
maximum frame length is the constant `Integer.MAX_VALUE`. There is no option to 
set a smaller one, so the decoder keeps every byte a peer sends until the two 
end bytes arrive, however long the message gets. The camel-netty `textline` 
codec has `decoderMaxLineLength` for the same purpose, and camel-mllp has 
`maxBufferSize`; the HL7 Netty decoder has nothing comparable.
   
   This PR adds a `maxFrameLength` option to the decoder factory. The default 
stays `Integer.MAX_VALUE`, so nothing changes for existing routes.
   
   Changes:
   - `HL7MLLPConfig` gets `maxFrameLength` (an `int`, default 
`Integer.MAX_VALUE`); its setter refuses 0 and negative values with an 
`IllegalArgumentException` (Netty refuses them too, but only when a channel is 
opened).
   - `HL7MLLPNettyDecoderFactory` gets 
`getMaxFrameLength()`/`setMaxFrameLength(int)`, and `HL7MLLPNettyDecoder` 
passes the value to `DelimiterBasedFrameDecoder` instead of the constant. The 
encoder factory and the Mina codec (`HL7MLLPCodec`) are not changed.
   - Semantics are those of Netty's `DelimiterBasedFrameDecoder` with fail-fast 
(the constructor the decoder already used): the length is counted from the 
start of the data up to the end bytes (the start byte included, the end bytes 
not); a message of exactly `maxFrameLength` bytes is decoded, one byte more 
fails. As soon as more than `maxFrameLength` bytes have arrived without the end 
bytes, the decoder throws a `TooLongFrameException` and discards the data up to 
the next end bytes. A Netty consumer (`ServerChannelHandler.exceptionCaught`) 
passes the exception to its exception handler ("Closing channel as an exception 
was thrown from Netty") and closes the connection; the route does not see the 
message, and the sender gets no ACK. On a Netty producer that uses the decoder 
for replies, the exchange fails with the exception 
(`ClientChannelHandler.exceptionCaught`, read, not run).
   - Docs: a `maxFrameLength` row in the HL7 MLLP codec table of 
`hl7-dataformat.adoc` (and the catalog copy, identical), marked "Netty decoder 
only". It also says that a message of exactly the limit fails when its first 
end byte arrives in a read of its own (then more than `maxFrameLength` bytes 
are there without both end bytes), so the limit should have some margin. I 
checked this with a decoder on an `EmbeddedChannel`: a start byte and 49 bytes 
with a limit of 50, then `0x1c` alone, gives `TooLongFrameException: frame 
length exceeds 50: 51 - discarded`; with 48 bytes the message is decoded. 
Passing `maxFrameLength + 1` to Netty and checking the decoded frame ourselves 
would avoid it; I kept Netty's own rule and documented it, but can change it if 
you prefer.
   - Name: `maxFrameLength` is the name of the Netty constructor parameter and 
of `ChannelHandlerFactories.newDelimiterBasedFrameDecoder` in camel-netty. 
camel-mllp's `maxBufferSize` (default 1 GiB) limits the buffer of its own 
socket reader, a different component with a different default.
   - No upgrade guide entry: the default is unchanged. No generated file other 
than the catalog copy of the doc (the factory is a plain bean, not a model 
option).
   
   Tests:
   - New `HL7MLLPNettyDecoderMaxFrameLengthTest`, 6 tests on an 
`EmbeddedChannel`: a message at the limit is decoded; one byte over fails with 
`TooLongFrameException` ("frame length exceeds 50") and nothing is decoded; 
data without end bytes fails once over the limit and the next message after the 
discarded rest is decoded; the factory applies its limit to the decoders it 
creates; the default is `Integer.MAX_VALUE` and a 1 MiB message is decoded; 0 
and -1 are refused.
   - New `HL7MLLPNettyMaxFrameLengthRouteTest`: a `netty:tcp` consumer with 
`decoders=#hl7decoder` (limit 1024) and a recording `exceptionHandler`. A 
socket sends a start byte and 2000 bytes without end bytes: the listener closes 
the connection, the exception handler gets a `TooLongFrameException`, and a 
normal HL7 request on a new connection is still answered with the ACK.
   - On main the new tests do not compile (`setMaxFrameLength` does not exist). 
With the decoder passing `Integer.MAX_VALUE` again (the value of main) and 
everything else kept, 4 of 7 fail: `messageOverTheLimitFails`, 
`dataWithoutEndBytesFailsOnceOverTheLimitAndTheNextMessageIsDecoded` and 
`factoryAppliesTheLimitToItsDecoders` ("Expected 
io.netty.handler.codec.TooLongFrameException to be thrown, but nothing was 
thrown"), and the route test (`SocketTimeoutException: Read timed out`: the 
connection is never closed). The at-limit, default and validation tests pass 
there, as they should.
   - camel-hl7 with the change: 79 tests, 0 failures, 0 skipped. JUnit 
assertions as in the module, no `Thread.sleep`.
   - I built offline with `-Djackson3-version=3.2.3 -Dmina-version=2.2.9` (not 
in my local repository at the versions of the pom).
   
   Backport candidates, for the maintainers to label if wanted (contributors 
cannot set `port/*` labels): `HL7MLLPNettyDecoder` has the same 
`MAX_FRAME_LENGTH = Integer.MAX_VALUE` constant on camel-4.23.x, 4.22.x and 
4.18.x (read, not run). As a new option it is for main; backporting is up to 
you.
   
   # Target
   
   - [x] I checked that the commit is targeting the correct branch (Camel 4 
uses the `main` branch)
   
   # Tracking
   - [x] If this is a large change, bug fix, or code improvement, I checked 
there is a [JIRA issue](https://issues.apache.org/jira/browse/CAMEL) filed for 
the change (usually before you start working on it): 
[CAMEL-26090](https://issues.apache.org/jira/browse/CAMEL-26090).
   
   # Apache Camel coding standards and style
   
   - [x] I checked that each commit in the pull request has a meaningful 
subject line and body.
   - [ ] I have run `mvn clean install -DskipTests` locally from root folder 
and I have committed all auto-generated changes.
     (I built camel-hl7 and the modules it needs and ran the camel-hl7 tests, 
including the formatter and import-sort plugins. The only generated file, the 
catalog copy of `hl7-dataformat.adoc`, is identical to the source doc. I did 
not run the full root build.)
   
   # AI-assisted contributions
   
   - [x] If this PR includes AI-generated code, commits have proper 
co-authorship attribution (e.g., `Co-authored-by` trailers) and the PR 
description identifies the AI tool used.
     This PR was prepared with Claude Code (Claude Opus 5.5). The commit 
carries a `Co-Authored-By` trailer.
   
   _Claude Code on behalf of allthingssecurity_
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to