ring 2024-06-10

Is it possible to use compojure with jetty run with :async? set to true? Returning a CompletableFuture from a route does not seem to work

AFAIK only interceptors support automatic lifting Fortunately writing a middleware to go back from CF to CPS isn't hard, just take care to run the callback on the right thread pool, if that matters

compojure created handlers support the async arities

Returning a completable future is not a thing, async in ring is via callbacks

what does a routes definition look like that does the response via an async callback?

(GET "/foo" [] ...)

or when doing async callbacks do you do it via handlers directly and stop using routes?

if there's an end-to-end example somewhere of using run-jetty with async? true and compojure handlers doing async responses that would be really helpful

I haven't been able to find one

a route just creates a handler

the handler takes the ring async arities

for async a ring handler takes 3 args, a request map and a success and failure callback

Compojure uses a protocol, compojure.response/Sendable, to work with Ring's 3-arity async form. By default it supports functions, so:

(GET "/async" []
  (fn [_req respond _raise]
    (respond {:status 200, :headers {}, :body "foo"})))
This could be extended to CompletableFuture, or any other async mechanism. Alternatively, you could write middleware that looks for response bodies of a certain type (such as CompletableFuture) and handle the conversion. However I don't know enough about CompletableFuture and how to interact with it with Clojure to produce an example off the cuff, I'm afraid.

Another option is to extend the ring.core.protocols/StreamableResponseBody protocol with support for CompletableFuture. That can be used in both synchronous mode (in which case the thread will block), or in asynchronous mode.

This is assuming you want CompletableFuture in particular, and not some other asynchronous mechanism.

Thanks. I don't particularly care about CompletableFuture and only mentioned that because I believe that's what Jetty uses for async stuff.