Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ContentSourcePublisher throws from request #11363

Closed
gregw opened this issue Feb 1, 2024 · 7 comments · Fixed by #11369
Closed

ContentSourcePublisher throws from request #11363

gregw opened this issue Feb 1, 2024 · 7 comments · Fixed by #11369
Labels
Bug For general bugs on Jetty side

Comments

@gregw
Copy link
Contributor

gregw commented Feb 1, 2024

Jetty version(s)

Jetty Environment
12

Description

In the spring integration branch we are seeing the following exception:

java.lang.IllegalStateException: channel already completed
	at org.eclipse.jetty.server.internal.HttpChannelState$ChannelRequest.lockedGetHttpChannelState(HttpChannelState.java:809) ~[jetty-server-12.0.6.jar:12.0.6]
	at org.eclipse.jetty.server.internal.HttpChannelState$ChannelRequest.read(HttpChannelState.java:873) ~[jetty-server-12.0.6.jar:12.0.6]
	at org.eclipse.jetty.io.content.ContentSourcePublisher$SubscriptionImpl.process(ContentSourcePublisher.java:114) ~[jetty-io-12.0.6.jar:12.0.6]
	at org.eclipse.jetty.io.content.ContentSourcePublisher$SubscriptionImpl.request(ContentSourcePublisher.java:85) ~[jetty-io-12.0.6.jar:12.0.6]
	at org.reactivestreams.FlowAdapters$ReactiveToFlowSubscription.request(FlowAdapters.java:182) ~[reactive-streams-1.0.4.jar:?]
	at reactor.core.publisher.FluxMap$MapSubscriber.request(FluxMap.java:164) ~[reactor-core-3.6.2.jar:3.6.2]
	at reactor.core.publisher.BaseSubscriber.request(BaseSubscriber.java:214) ~[reactor-core-3.6.2.jar:3.6.2]
	at org.springframework.http.codec.multipart.MultipartParser.requestBuffer(MultipartParser.java:196) ~[spring-web-6.1.4-SNAPSHOT.jar:6.1.4-SNAPSHOT]

However, the reactive specification says that "Calling Subscription.request MUST return normally". So our ContentSourcePublisher is breaking that rule and throwing rather than calling onError

@gregw gregw added the Bug For general bugs on Jetty side label Feb 1, 2024
@gregw
Copy link
Contributor Author

gregw commented Feb 1, 2024

@sbordet do you agree this is a bug?
@lachlan-roberts I'm not sure if this is causing some spring-framework integration failures, but it is certainly not helping us fail in a normal way.

@gregw
Copy link
Contributor Author

gregw commented Feb 1, 2024

Actually @lorban do you think this is a violation of our Content.Source#read() contract? Should it return an error chunk rather than throw?

@sbordet
Copy link
Contributor

sbordet commented Feb 1, 2024

We throw for "must not happen" kind of situations.

Returning an error chunk is more like a failure that may happen (e.g. early EOF).

I agree that we should protect a call to request(), but the fact that we have a visible stack trace is actually a plus, rather than maybe a warning log that may not be seen.

Why there is a call to request() with the channel already completed?

@joakime
Copy link
Contributor

joakime commented Feb 1, 2024

Interesting discussion back in 2014 about throwing from the reactive Subscription APIs.
reactive-streams/reactive-streams-jvm#52

We throw for "must not happen" kind of situations.

When I first read this, it felt like you were describing a fatal error.

To the reactive folks back in 2014 a fatal error is only something that will have a high likelyhood of shutting down the JVM. (think OOM, VMError, etc)
But then a bunch of examples of fatal (but not JVM failures) were presented in the above discussion, but it didn't sway the group to allow throwing from any Subscription API.
Basically, while the discussion was focused on specifically onError() the behaviors of the rest of the API and how the API is used pretty much says we cannot throw, even in the "must not happen" types, and must use onError to report that.

@lorban
Copy link
Contributor

lorban commented Feb 1, 2024

IMHO Content.Source#read() should never throw for at least two reasons:

  • Consistency with methods taking a callback: they always fail the callback and never throw.
  • Clear out uncertainty: there's only one way to be notified of errors, so you don't have to wear a belt (isError()) & suspenders (try / catch).

@gregw
Copy link
Contributor Author

gregw commented Feb 2, 2024

We throw for "must not happen" kind of situations.

Returning an error chunk is more like a failure that may happen (e.g. early EOF).

I agree that we should protect a call to request(), but the fact that we have a visible stack trace is actually a plus, rather than maybe a warning log that may not be seen.

Why there is a call to request() with the channel already completed?

@sbordet I've not yet looked into the cause of the late call to read, but it is from a unit test and for all we know it could be testing error handling. I.e. what happens if the connection closes early.

I was looking for a normal termination of the Flux, but didn't see one.. but thought that we should say least see an onError

I'd expect the stack trace would still be visible if it was reported to onError and was not expected.

I'll prepare a branch with this fixed and see how that affects the spring test... Probably won't fix it, but we'll see.

@gregw
Copy link
Contributor Author

gregw commented Feb 2, 2024

  • Clear out uncertainty: there's only one way to be notified of errors, so you don't have to wear a belt (isError()) & suspenders (try / catch).

and if you only catch Exception you can still end up with your pants around your ankles if somebody throws a Runtime!

I'll fix

gregw added a commit that referenced this issue Feb 2, 2024
Fixes #11363 by ensuring that read never throws, but instead returns an Error chunk.
gregw added a commit that referenced this issue Feb 19, 2024
Fixes #11363 by ensuring that read never throws, but instead returns an Error chunk.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Bug For general bugs on Jetty side
Projects
No open projects
Status: ✅ Done
Development

Successfully merging a pull request may close this issue.

4 participants