This is an automated email from the ASF dual-hosted git repository.
oscerd pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-kamelets.git
The following commit(s) were added to refs/heads/main by this push:
new ae5e301c0 Fix #3086: Parser actions: add maxDocumentSize, checked
before the parse (#3087)
ae5e301c0 is described below
commit ae5e301c0b338a078a3b80b6740a64f2ace732ec
Author: jiriOndrusek-agent <[email protected]>
AuthorDate: Mon Oct 12 10:57:16 2026 +0200
Fix #3086: Parser actions: add maxDocumentSize, checked before the parse
(#3087)
* Fix #3086: Parser actions: add maxDocumentSize, checked before the parse
tika-extract-text-action and docling-convert-action reject a payload
larger than maxDocumentSize bytes before the parse or conversion would
materialize it; the exchange fails with an IllegalArgumentException
naming the document. The guard is a choice in precondition mode, so it
exists only when the cap is set. It measures with length(): free for
byte arrays and cached streams, a streamed count otherwise, and it never
trusts a CamelFileLength header a caller could forge. Kamelet twin of
the raw-size guard in the Camel Quarkus langchain4j-ingest extension
(apache/camel-quarkus#9152).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Fable 5.1 <[email protected]>
* Fix #3086: Parser actions: measure an uncached stream without draining it
length() reads a stream body to its end and closes it, so with stream
caching switched off a raw stream under the cap reached Tika or Docling
empty. Such a stream is now read into bytes before it is measured;
files, byte arrays and cached streams are still measured in place. A
comment says why the cap check must stay a precondition. New Citrus
test parser-max-size-stream-route covers raw streams with stream
caching off.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
---------
Co-authored-by: Claude Opus 5.5 <[email protected]>
---
kamelets/docling-convert-action.kamelet.yaml | 42 ++++++++-
kamelets/tika-extract-text-action.kamelet.yaml | 41 ++++++++-
.../kamelets/docling-convert-action.kamelet.yaml | 42 ++++++++-
.../kamelets/tika-extract-text-action.kamelet.yaml | 41 ++++++++-
.../parser-max-size-route.citrus.it.yaml | 101 +++++++++++++++++++++
.../langchain4j-ingest/parser-max-size-route.yaml | 84 +++++++++++++++++
.../parser-max-size-stream-route.citrus.it.yaml | 78 ++++++++++++++++
.../parser-max-size-stream-route.yaml | 66 ++++++++++++++
8 files changed, 491 insertions(+), 4 deletions(-)
diff --git a/kamelets/docling-convert-action.kamelet.yaml
b/kamelets/docling-convert-action.kamelet.yaml
index 706985f92..6f6a3e0b6 100644
--- a/kamelets/docling-convert-action.kamelet.yaml
+++ b/kamelets/docling-convert-action.kamelet.yaml
@@ -41,7 +41,8 @@ spec:
the body is pinned to bytes and the `CamelDocling*` control headers are
swept, because
none of them may be decided by a consumer-delivered payload. By default
the `docling`
component executes a local docling CLI; set `doclingServeUrl` to use a
Docling Serve
- instance instead - the natural fit for self-contained Pipes.
+ instance instead - the natural fit for self-contained Pipes. With
`maxDocumentSize`
+ set, a payload larger than that many bytes fails the exchange before the
conversion.
type: object
properties:
documentIdHeader:
@@ -50,6 +51,13 @@ spec:
CamelLangChain4jIngestDocumentId exchange property before the
conversion.
type: string
default: CamelLangChain4jIngestDocumentId
+ maxDocumentSize:
+ title: Max Document Size
+ description: Maximum size of the payload in bytes, checked before the
conversion; a
+ larger document fails the exchange without being converted. 0, the
default, means no
+ limit.
+ type: integer
+ default: 0
doclingServeUrl:
title: Docling Serve URL
description: Address of a Docling Serve instance to convert through,
such as
@@ -74,6 +82,38 @@ spec:
expression:
simple:
expression: "${header[{{documentIdHeader}}]}"
+ # the size cap, present only when maxDocumentSize is set: the
conversion materializes
+ # the document, so an oversized one is rejected first. The
precondition is evaluated
+ # once, when the Kamelet is instantiated - only then does
${properties:maxDocumentSize:0}
+ # see the parameter, at runtime it resolves to 0 - so the check must
stay a
+ # precondition, and the comparison below uses {{maxDocumentSize}}
+ - choice:
+ precondition: true
+ when:
+ - expression:
+ simple:
+ expression: "${properties:maxDocumentSize:0} > 0"
+ steps:
+ # length() reads a stream to its end and closes it, so a
stream body that
+ # stream caching (on by default) left uncached is read into
bytes first; files,
+ # byte arrays and cached streams are measured in place,
without a copy
+ - choice:
+ when:
+ - expression:
+ simple:
+ expression: "${body} is 'java.io.InputStream' &&
${body} !is 'org.apache.camel.StreamCache'"
+ steps:
+ - convertBodyTo:
+ type: byte[]
+ - choice:
+ when:
+ - expression:
+ simple:
+ expression: "${length()} > {{maxDocumentSize}}"
+ steps:
+ - throwException:
+ exceptionType:
java.lang.IllegalArgumentException
+ message: "Document
'${exchangeProperty.CamelLangChain4jIngestDocumentId}' exceeds maxDocumentSize
(${length()} > {{maxDocumentSize}} bytes)"
- convertBodyTo:
type: byte[]
- removeHeaders:
diff --git a/kamelets/tika-extract-text-action.kamelet.yaml
b/kamelets/tika-extract-text-action.kamelet.yaml
index b959b8875..1cc141923 100644
--- a/kamelets/tika-extract-text-action.kamelet.yaml
+++ b/kamelets/tika-extract-text-action.kamelet.yaml
@@ -42,7 +42,8 @@ spec:
after the parse all non-Camel headers are removed: past this action,
parsed document
metadata is indistinguishable from caller headers, so it must not travel
further. Each
format needs its Tika parser module on the classpath; camel-tika ships
the HTML and
- text modules, PDF needs org.apache.tika:tika-parser-pdf-module.
+ text modules, PDF needs org.apache.tika:tika-parser-pdf-module. With
`maxDocumentSize`
+ set, a payload larger than that many bytes fails the exchange before the
parse.
type: object
properties:
documentIdHeader:
@@ -51,6 +52,12 @@ spec:
CamelLangChain4jIngestDocumentId exchange property before the parse.
type: string
default: CamelLangChain4jIngestDocumentId
+ maxDocumentSize:
+ title: Max Document Size
+ description: Maximum size of the payload in bytes, checked before the
parse; a larger
+ document fails the exchange without being parsed. 0, the default,
means no limit.
+ type: integer
+ default: 0
dependencies:
- "camel:tika"
- "camel:kamelet"
@@ -65,6 +72,38 @@ spec:
expression:
simple:
expression: "${header[{{documentIdHeader}}]}"
+ # the size cap, present only when maxDocumentSize is set: the parse
materializes the
+ # document, so an oversized one is rejected first. The precondition is
evaluated once,
+ # when the Kamelet is instantiated - only then does
${properties:maxDocumentSize:0} see
+ # the parameter, at runtime it resolves to 0 - so the check must stay
a precondition,
+ # and the comparison below uses {{maxDocumentSize}}
+ - choice:
+ precondition: true
+ when:
+ - expression:
+ simple:
+ expression: "${properties:maxDocumentSize:0} > 0"
+ steps:
+ # length() reads a stream to its end and closes it, so a
stream body that
+ # stream caching (on by default) left uncached is read into
bytes first; files,
+ # byte arrays and cached streams are measured in place,
without a copy
+ - choice:
+ when:
+ - expression:
+ simple:
+ expression: "${body} is 'java.io.InputStream' &&
${body} !is 'org.apache.camel.StreamCache'"
+ steps:
+ - convertBodyTo:
+ type: byte[]
+ - choice:
+ when:
+ - expression:
+ simple:
+ expression: "${length()} > {{maxDocumentSize}}"
+ steps:
+ - throwException:
+ exceptionType:
java.lang.IllegalArgumentException
+ message: "Document
'${exchangeProperty.CamelLangChain4jIngestDocumentId}' exceeds maxDocumentSize
(${length()} > {{maxDocumentSize}} bytes)"
# tika answers plain text (body only, blocks newline-separated) at the
pinned encoding
- to:
uri: tika
diff --git
a/library/camel-kamelets/src/main/resources/kamelets/docling-convert-action.kamelet.yaml
b/library/camel-kamelets/src/main/resources/kamelets/docling-convert-action.kamelet.yaml
index 706985f92..6f6a3e0b6 100644
---
a/library/camel-kamelets/src/main/resources/kamelets/docling-convert-action.kamelet.yaml
+++
b/library/camel-kamelets/src/main/resources/kamelets/docling-convert-action.kamelet.yaml
@@ -41,7 +41,8 @@ spec:
the body is pinned to bytes and the `CamelDocling*` control headers are
swept, because
none of them may be decided by a consumer-delivered payload. By default
the `docling`
component executes a local docling CLI; set `doclingServeUrl` to use a
Docling Serve
- instance instead - the natural fit for self-contained Pipes.
+ instance instead - the natural fit for self-contained Pipes. With
`maxDocumentSize`
+ set, a payload larger than that many bytes fails the exchange before the
conversion.
type: object
properties:
documentIdHeader:
@@ -50,6 +51,13 @@ spec:
CamelLangChain4jIngestDocumentId exchange property before the
conversion.
type: string
default: CamelLangChain4jIngestDocumentId
+ maxDocumentSize:
+ title: Max Document Size
+ description: Maximum size of the payload in bytes, checked before the
conversion; a
+ larger document fails the exchange without being converted. 0, the
default, means no
+ limit.
+ type: integer
+ default: 0
doclingServeUrl:
title: Docling Serve URL
description: Address of a Docling Serve instance to convert through,
such as
@@ -74,6 +82,38 @@ spec:
expression:
simple:
expression: "${header[{{documentIdHeader}}]}"
+ # the size cap, present only when maxDocumentSize is set: the
conversion materializes
+ # the document, so an oversized one is rejected first. The
precondition is evaluated
+ # once, when the Kamelet is instantiated - only then does
${properties:maxDocumentSize:0}
+ # see the parameter, at runtime it resolves to 0 - so the check must
stay a
+ # precondition, and the comparison below uses {{maxDocumentSize}}
+ - choice:
+ precondition: true
+ when:
+ - expression:
+ simple:
+ expression: "${properties:maxDocumentSize:0} > 0"
+ steps:
+ # length() reads a stream to its end and closes it, so a
stream body that
+ # stream caching (on by default) left uncached is read into
bytes first; files,
+ # byte arrays and cached streams are measured in place,
without a copy
+ - choice:
+ when:
+ - expression:
+ simple:
+ expression: "${body} is 'java.io.InputStream' &&
${body} !is 'org.apache.camel.StreamCache'"
+ steps:
+ - convertBodyTo:
+ type: byte[]
+ - choice:
+ when:
+ - expression:
+ simple:
+ expression: "${length()} > {{maxDocumentSize}}"
+ steps:
+ - throwException:
+ exceptionType:
java.lang.IllegalArgumentException
+ message: "Document
'${exchangeProperty.CamelLangChain4jIngestDocumentId}' exceeds maxDocumentSize
(${length()} > {{maxDocumentSize}} bytes)"
- convertBodyTo:
type: byte[]
- removeHeaders:
diff --git
a/library/camel-kamelets/src/main/resources/kamelets/tika-extract-text-action.kamelet.yaml
b/library/camel-kamelets/src/main/resources/kamelets/tika-extract-text-action.kamelet.yaml
index b959b8875..1cc141923 100644
---
a/library/camel-kamelets/src/main/resources/kamelets/tika-extract-text-action.kamelet.yaml
+++
b/library/camel-kamelets/src/main/resources/kamelets/tika-extract-text-action.kamelet.yaml
@@ -42,7 +42,8 @@ spec:
after the parse all non-Camel headers are removed: past this action,
parsed document
metadata is indistinguishable from caller headers, so it must not travel
further. Each
format needs its Tika parser module on the classpath; camel-tika ships
the HTML and
- text modules, PDF needs org.apache.tika:tika-parser-pdf-module.
+ text modules, PDF needs org.apache.tika:tika-parser-pdf-module. With
`maxDocumentSize`
+ set, a payload larger than that many bytes fails the exchange before the
parse.
type: object
properties:
documentIdHeader:
@@ -51,6 +52,12 @@ spec:
CamelLangChain4jIngestDocumentId exchange property before the parse.
type: string
default: CamelLangChain4jIngestDocumentId
+ maxDocumentSize:
+ title: Max Document Size
+ description: Maximum size of the payload in bytes, checked before the
parse; a larger
+ document fails the exchange without being parsed. 0, the default,
means no limit.
+ type: integer
+ default: 0
dependencies:
- "camel:tika"
- "camel:kamelet"
@@ -65,6 +72,38 @@ spec:
expression:
simple:
expression: "${header[{{documentIdHeader}}]}"
+ # the size cap, present only when maxDocumentSize is set: the parse
materializes the
+ # document, so an oversized one is rejected first. The precondition is
evaluated once,
+ # when the Kamelet is instantiated - only then does
${properties:maxDocumentSize:0} see
+ # the parameter, at runtime it resolves to 0 - so the check must stay
a precondition,
+ # and the comparison below uses {{maxDocumentSize}}
+ - choice:
+ precondition: true
+ when:
+ - expression:
+ simple:
+ expression: "${properties:maxDocumentSize:0} > 0"
+ steps:
+ # length() reads a stream to its end and closes it, so a
stream body that
+ # stream caching (on by default) left uncached is read into
bytes first; files,
+ # byte arrays and cached streams are measured in place,
without a copy
+ - choice:
+ when:
+ - expression:
+ simple:
+ expression: "${body} is 'java.io.InputStream' &&
${body} !is 'org.apache.camel.StreamCache'"
+ steps:
+ - convertBodyTo:
+ type: byte[]
+ - choice:
+ when:
+ - expression:
+ simple:
+ expression: "${length()} > {{maxDocumentSize}}"
+ steps:
+ - throwException:
+ exceptionType:
java.lang.IllegalArgumentException
+ message: "Document
'${exchangeProperty.CamelLangChain4jIngestDocumentId}' exceeds maxDocumentSize
(${length()} > {{maxDocumentSize}} bytes)"
# tika answers plain text (body only, blocks newline-separated) at the
pinned encoding
- to:
uri: tika
diff --git
a/tests/camel-kamelets-itest/src/test/resources/langchain4j-ingest/parser-max-size-route.citrus.it.yaml
b/tests/camel-kamelets-itest/src/test/resources/langchain4j-ingest/parser-max-size-route.citrus.it.yaml
new file mode 100644
index 000000000..722ae4800
--- /dev/null
+++
b/tests/camel-kamelets-itest/src/test/resources/langchain4j-ingest/parser-max-size-route.citrus.it.yaml
@@ -0,0 +1,101 @@
+# ---------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ---------------------------------------------------------------------------
+
+name: parser-max-size-route-test
+actions:
+ # drain any request a previous test may have left on the shared server
+ - purge:
+ endpoints:
+ - name: "httpServer"
+ timeout: 100
+ - createVariables:
+ variables:
+ - name: "docling.serve.url"
+ value: "http://localhost:${http.server.port}"
+ - name: "report.url"
+ value: "http://localhost:${http.server.port}/result/parser-size"
+ # a fresh directory per test run, see the file-source test
+ - name: "ingest.dir"
+ value: "ingest-size-citrus:randomNumber(5)"
+
+ - camel:
+ cli:
+ run:
+ waitForRunningState: false
+ args:
+ - "--max-seconds=60"
+ integration:
+ file: "langchain4j-ingest/parser-max-size-route.yaml"
+ systemProperties:
+ properties:
+ - name: "docling.serve.url"
+ value: "${docling.serve.url}"
+ - name: "report.url"
+ value: "${report.url}"
+ - name: "ingest.dir"
+ value: "${ingest.dir}"
+
+ # under the cap: parsed as usual
+ - http:
+ server: "httpServer"
+ receiveRequest:
+ POST:
+ path: "/result/parser-size"
+ type: "plaintext"
+ body:
+ data: "@contains('parsed: Short text.')@"
+ - http:
+ server: "httpServer"
+ sendResponse:
+ response:
+ status: 200
+ reasonPhrase: "OK"
+ version: "HTTP/1.1"
+
+ # over the cap: rejected before the conversion; a conversion request instead
would fail here
+ - http:
+ server: "httpServer"
+ receiveRequest:
+ POST:
+ path: "/result/parser-size"
+ type: "plaintext"
+ body:
+ data: "@matches('rejected: Document .long.pdf. exceeds
maxDocumentSize .100 > 40 bytes.')@"
+ - http:
+ server: "httpServer"
+ sendResponse:
+ response:
+ status: 200
+ reasonPhrase: "OK"
+ version: "HTTP/1.1"
+
+ # a file over the cap: rejected before the parse
+ - http:
+ server: "httpServer"
+ receiveRequest:
+ POST:
+ path: "/result/parser-size"
+ type: "plaintext"
+ body:
+ data: "@matches('rejected: Document .big.txt. exceeds
maxDocumentSize .100 > 40 bytes.')@"
+ - http:
+ server: "httpServer"
+ sendResponse:
+ response:
+ status: 200
+ reasonPhrase: "OK"
+ version: "HTTP/1.1"
diff --git
a/tests/camel-kamelets-itest/src/test/resources/langchain4j-ingest/parser-max-size-route.yaml
b/tests/camel-kamelets-itest/src/test/resources/langchain4j-ingest/parser-max-size-route.yaml
new file mode 100644
index 000000000..da97a95b7
--- /dev/null
+++
b/tests/camel-kamelets-itest/src/test/resources/langchain4j-ingest/parser-max-size-route.yaml
@@ -0,0 +1,84 @@
+# ---------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ---------------------------------------------------------------------------
+
+# maxDocumentSize on the parser actions: the cap is checked before the parse
or conversion.
+# One route feeds three documents in turn, each report waiting for the test's
answer, so
+# the order is fixed: a short text through Tika under the cap, an oversized
payload through
+# Docling (pointed at the test's HTTP server, so a payload leaking past the
cap would show up
+# there as a conversion request), and an oversized file through the file
source and Tika.
+- route:
+ id: parser-max-size-route
+ from:
+ uri: "timer:size"
+ parameters:
+ repeatCount: 1
+ steps:
+ - setBody:
+ constant: "Short text."
+ - setHeader:
+ name: "CamelLangChain4jIngestDocumentId"
+ constant: "short.txt"
+ - doTry:
+ steps:
+ - to: "kamelet:tika-extract-text-action?maxDocumentSize=40"
+ - setBody:
+ simple: "parsed: ${body}"
+ doCatch:
+ - exception:
+ - "java.lang.IllegalArgumentException"
+ steps:
+ - setBody:
+ simple: "rejected: ${exception.message}"
+ - to: "{{report.url}}"
+ # 100 bytes, over the cap of 40
+ - setBody:
+ constant:
"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
+ - setHeader:
+ name: "CamelLangChain4jIngestDocumentId"
+ constant: "long.pdf"
+ - doTry:
+ steps:
+ - to:
"kamelet:docling-convert-action?maxDocumentSize=40&useDoclingServe=true&doclingServeUrl={{docling.serve.url}}"
+ - setBody:
+ simple: "converted: ${body}"
+ doCatch:
+ - exception:
+ - "java.lang.IllegalArgumentException"
+ steps:
+ - setBody:
+ simple: "rejected: ${exception.message}"
+ - to: "{{report.url}}"
+ - setBody:
+ constant:
"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
+ - to: "file:{{ingest.dir}}?fileName=big.txt"
+- route:
+ id: parser-max-size-file-route
+ from:
+ uri: "kamelet:langchain4j-ingest-file-source?directory={{ingest.dir}}"
+ steps:
+ - doTry:
+ steps:
+ - to: "kamelet:tika-extract-text-action?maxDocumentSize=40"
+ - setBody:
+ simple: "parsed: ${body}"
+ doCatch:
+ - exception:
+ - "java.lang.IllegalArgumentException"
+ steps:
+ - setBody:
+ simple: "rejected: ${exception.message}"
+ - to: "{{report.url}}"
diff --git
a/tests/camel-kamelets-itest/src/test/resources/langchain4j-ingest/parser-max-size-stream-route.citrus.it.yaml
b/tests/camel-kamelets-itest/src/test/resources/langchain4j-ingest/parser-max-size-stream-route.citrus.it.yaml
new file mode 100644
index 000000000..91fb30293
--- /dev/null
+++
b/tests/camel-kamelets-itest/src/test/resources/langchain4j-ingest/parser-max-size-stream-route.citrus.it.yaml
@@ -0,0 +1,78 @@
+# ---------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ---------------------------------------------------------------------------
+
+name: parser-max-size-stream-route-test
+actions:
+ # drain any request a previous test may have left on the shared server
+ - purge:
+ endpoints:
+ - name: "httpServer"
+ timeout: 100
+ - createVariables:
+ variables:
+ - name: "report.url"
+ value:
"http://localhost:${http.server.port}/result/parser-size-stream"
+
+ # stream caching off: the actions see raw streams, not cached ones
+ - camel:
+ cli:
+ run:
+ waitForRunningState: false
+ args:
+ - "--max-seconds=60"
+ integration:
+ file: "langchain4j-ingest/parser-max-size-stream-route.yaml"
+ systemProperties:
+ properties:
+ - name: "report.url"
+ value: "${report.url}"
+ - name: "camel.main.streamCachingEnabled"
+ value: "false"
+
+ # under the cap: the whole stream reaches the parser
+ - http:
+ server: "httpServer"
+ receiveRequest:
+ POST:
+ path: "/result/parser-size-stream"
+ type: "plaintext"
+ body:
+ data: "@contains('parsed: Short text.')@"
+ - http:
+ server: "httpServer"
+ sendResponse:
+ response:
+ status: 200
+ reasonPhrase: "OK"
+ version: "HTTP/1.1"
+
+ # over the cap: rejected, the size measured once and reported right
+ - http:
+ server: "httpServer"
+ receiveRequest:
+ POST:
+ path: "/result/parser-size-stream"
+ type: "plaintext"
+ body:
+ data: "@matches('rejected: Document .long.txt. exceeds
maxDocumentSize .100 > 40 bytes.')@"
+ - http:
+ server: "httpServer"
+ sendResponse:
+ response:
+ status: 200
+ reasonPhrase: "OK"
+ version: "HTTP/1.1"
diff --git
a/tests/camel-kamelets-itest/src/test/resources/langchain4j-ingest/parser-max-size-stream-route.yaml
b/tests/camel-kamelets-itest/src/test/resources/langchain4j-ingest/parser-max-size-stream-route.yaml
new file mode 100644
index 000000000..38a464f50
--- /dev/null
+++
b/tests/camel-kamelets-itest/src/test/resources/langchain4j-ingest/parser-max-size-stream-route.yaml
@@ -0,0 +1,66 @@
+# ---------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one or more
+# contributor license agreements. See the NOTICE file distributed with
+# this work for additional information regarding copyright ownership.
+# The ASF licenses this file to You under the Apache License, Version 2.0
+# (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+# ---------------------------------------------------------------------------
+
+# maxDocumentSize with stream caching switched off, so a stream body reaches
the action raw
+# instead of cached. A raw stream under the cap must still be parsed whole -
the measurement
+# must not drain it - and one over the cap is rejected with its exact size.
+- route:
+ id: parser-max-size-stream-route
+ from:
+ uri: "timer:stream"
+ parameters:
+ repeatCount: 1
+ steps:
+ - setBody:
+ constant: "Short text."
+ - setBody:
+ simple: "${bodyAs(java.io.InputStream)}"
+ - setHeader:
+ name: "CamelLangChain4jIngestDocumentId"
+ constant: "short.txt"
+ - doTry:
+ steps:
+ - to: "kamelet:tika-extract-text-action?maxDocumentSize=40"
+ - setBody:
+ simple: "parsed: ${body}"
+ doCatch:
+ - exception:
+ - "java.lang.IllegalArgumentException"
+ steps:
+ - setBody:
+ simple: "rejected: ${exception.message}"
+ - to: "{{report.url}}"
+ # 100 bytes, over the cap of 40
+ - setBody:
+ constant:
"0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789"
+ - setBody:
+ simple: "${bodyAs(java.io.InputStream)}"
+ - setHeader:
+ name: "CamelLangChain4jIngestDocumentId"
+ constant: "long.txt"
+ - doTry:
+ steps:
+ - to: "kamelet:tika-extract-text-action?maxDocumentSize=40"
+ - setBody:
+ simple: "parsed: ${body}"
+ doCatch:
+ - exception:
+ - "java.lang.IllegalArgumentException"
+ steps:
+ - setBody:
+ simple: "rejected: ${exception.message}"
+ - to: "{{report.url}}"