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]
