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

Prevent IllegalStateException on close #12

Open
wants to merge 1 commit into
base: master
Choose a base branch
from

Conversation

dedeibel
Copy link

@dedeibel dedeibel commented Feb 6, 2021

TL;DR: The committed code will not close the inner output-stream, write-body-to-stream will close that one for us and since it is a proxy that calls complete on the AsyncContext we did trigger it twice, provoking the IllegalStateException

Please accept my pull request. I noticed a minor problem where an IllegalStateException was thrown after sending asynchronous data and then closing the channel. Code like the example from the readme.

I 21-02-06 17:33:42,843      async-dispatch-8 g.                  wrserver.http.invoke                    :sse/on-client-disconnect {:status 200, :headers {Content-Type text/event-stream; charset=UTF-8, Connection close, Cache-Control no-cache}, :body #object[clojure.core.async.impl.channels.ManyToManyChannel 0x51df0104 clojure.core.async.impl.channels.ManyToManyChannel@51df0104]}
Exception in thread "async-thread-macro-1" java.lang.IllegalStateException: AsyncContext completed and/or Request lifecycle recycled
	at org.eclipse.jetty.server.AsyncContextState.state(AsyncContextState.java:52)
	at org.eclipse.jetty.server.AsyncContextState.complete(AsyncContextState.java:72)
	at ring.util.servlet$make_output_stream$fn__9013.invoke(servlet.clj:91)
	at ring.util.servlet.proxy$java.io.FilterOutputStream$ff19274a.close(Unknown Source)
	at ring.sse$eval18682$fn__18683$fn__18684.invoke(sse.clj:14)
	at clojure.core.async$thread_call$fn__16270.invoke(async.clj:484)
	at clojure.lang.AFn.run(AFn.java:22)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
	at java.base/java.lang.Thread.run(Thread.java:834)

According to my analysis this might happen if complete is called multiple times. complete is called at the following point by ring: https://github.com/ring-clojure/ring/blob/1.9.0/ring-servlet/src/ring/util/servlet.clj#L129

It is called in a proxy method to the output stream. write-body-to-stream will call close on that stream for the user: https://github.com/ring-clojure/ring/blob/1.9.0/ring-core/src/ring/core/protocols.clj#L12

So when ring-sse puts output-stream inside of the with-open block it will be "closed" twice and complete will be called twice causing the Exception.

I did not get the exception with the following changes.

- Do not close the inner output-stream, write-body-to-stream will close that one for us
  and since it is a proxy that will call complete on the AsyncContext we triggered it
  twice, provoking the IllegalStateException
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant