[
https://issues.apache.org/jira/browse/CAMEL-24864?page=com.atlassian.jira.plugin.system.issuetabpanels:all-tabpanel
]
James Netherton resolved CAMEL-24864.
-------------------------------------
Resolution: Fixed
> camel-platform-http-vertx: response write failures are discarded, leaking
> exchanges and SSE subscribers when a client disconnects
> ---------------------------------------------------------------------------------------------------------------------------------
>
> Key: CAMEL-24864
> URL: https://issues.apache.org/jira/browse/CAMEL-24864
> Project: Camel
> Issue Type: Bug
> Components: camel-platform-http-vertx
> Reporter: James Netherton
> Assignee: James Netherton
> Priority: Minor
> Fix For: 4.23.0
>
>
> h3. Problem
> VertxPlatformHttpSupport writes an InputStream response body by piping an
> AsyncInputStream to the HttpServerResponse. This used the deprecated Pump,
> which
> calls writeStream.write(data) and discards the result, so every response write
> failure was invisible: the promise returned by writeResponse() completed
> successfully even when nothing reached the client.
> The consequence is that a client going away part way through a streaming
> response
> is never noticed by the consumer:
> * The write failure is swallowed, so the pipe is not terminated.
> * AsyncInputStream.close() is never called.
> * The exchange never completes (no doneUoW / releaseExchange).
> This is most visible for Server-Sent Events. A2AConsumer.setSseResponse puts
> an
> SseQueueInputStream on the message with content type text/event-stream. That
> stream only reaches EOF on an explicit end-of-stream marker from a terminal
> task
> state; otherwise it emits heartbeats indefinitely. Its close() is what
> triggers
> cleanup, via closeSubscriptionOnStreamClose -> setOnClose(...), which removes
> the
> subscriber. Because close() never runs when the client disconnects, the
> subscriber is never removed and the exchange never completes, so both leak for
> the lifetime of the application.
> h3. Reproduction
> Expose a platform-http endpoint whose body is an InputStream that keeps
> producing
> data (an SSE heartbeat stream, or any large download). Connect a client, read
> a
> few KB, then abort the connection. Observed behaviour: the stream's close() is
> never called, and the exchange stays open. Measured with a queue-backed
> heartbeat
> stream modelled on SseQueueInputStream: close() was not called within 15s of
> the
> client disconnecting.
> h3. Fix
> Replace Pump with pipe(), which propagates write failures to the completion
> promise, so the response write failure now reaches
> VertxPlatformHttpConsumer.handleFailure, the stream is closed and the exchange
> completes.
> Propagating the failure also makes a normal client disconnect look like an
> error,
> which it is not, so two follow-on problems are fixed as part of this:
> * A lost client connection must not be reported through the consumer's
> ExceptionHandler (WARN plus stack trace) and must not fail the routing
> context
> once the response head has been written. Note that ctx.response().closed()
> is
> not sufficient on its own to detect this: Netty fails the write promise
> before
> it reports the connection close to Vert.x, so the response is often not yet
> flagged as closed when the failure is handled. Failures are therefore
> classified by origin - a failure raised while reading the body stream is a
> real
> error, any other pipe failure is a failed response write.
> * AsyncInputStream threw IllegalStateException from its handler and read
> methods
> once closed, and logged "Unhandled error while processing stream" at ERROR
> for
> the in-flight read that fails when the channel is gone. Both are expected
> after
> a disconnect and should not be reported as errors.
--
This message was sent by Atlassian Jira
(v8.20.10#820010)