Fork me on GitHub
#pedestal
<
2016-08-12
>
hlship21:08:49

I’m having a problem in 0.5.0 where my interceptor may return a channel.

hlship21:08:26

I’m wrapping a normal Ring handler but trying to allow the handler to return a channel that conveys the response.

hlship21:08:35

(defn ^:private read-port?
  [v]
  (satisfies? ReadPort v))

(defn ^:private wrap-for-async
  "Invokes the function yielding the response.  Returns either the context with the :response
  key set, or a channel that will convey the context when ready (if the endpoint function returns
  a channel)."
  [f]
  (fn [context]
    (let [v (f context)]
      (if (read-port? v)
        (go (assoc context :response (<! v)))
        (assoc context :response v)))))

hlship21:08:37

So, this should work: my intereceptor builds off of this:

(interceptor {:name endpoint-kw
                  :enter (wrap-for-async generate-response)})

hlship21:08:04

generate-response takes the Pedestal context and returns just the Ring response map, or a channel that conveys that map.

hlship21:08:24

It’s not working in practice; if my handler returns a map, that passes through the interceptors just fine.

hlship21:08:49

If it returns a channel that conveys the map, I see a nil passing back up.

hlship21:08:59

One moment while I get some logging ...

hlship21:08:34

DEBUG io.pedestal.interceptor.chain - {:in begin, :execution-id 13, :line 291}
DEBUG io.pedestal.interceptor.chain - {:in enter-all, :execution-id 13, :line 137}
DEBUG io.pedestal.interceptor.chain - {:interceptor :io.pedestal.http.route/router, :stage :enter, :execution-id 13, :fn #object[io.pedestal.http.route$eval12858$fn__12859$fn__12860 0xbacd2c6 "io.pedestal.http.route$eval12858$fn__12859$fn__12860@bacd2c6"], :line 48}
DEBUG io.pedestal.interceptor.chain - {:in enter-all, :execution-id 13, :line 137}
DEBUG io.pedestal.interceptor.chain - {:interceptor :sample.async-widgets/view-widget, :stage :enter, :execution-id 13, :fn #object[io.aviso.rook$wrap_for_async$fn__11267 0x73b76e78 "io.aviso.rook$wrap_for_async$fn__11267@73b76e78"], :line 48}
DEBUG io.pedestal.interceptor.chain - {:in enter-all, :execution-id 13, :line 137}
DEBUG io.pedestal.interceptor.chain - {:in leave-all, :execution-id 13, :line 218}
DEBUG io.pedestal.interceptor.chain - {:in end, :execution-id 13, :context-keys (:request :io.pedestal.interceptor.chain/execution-id :io.pedestal.interceptor.chain/stack :route :url-for :bindings :response), :line 298}

hlship21:08:16

Hm. It’s got to be something in my code, because I see an interceptor around the endpoint get invoked and it has the correct :response key on the context.

hlship21:08:23

Maybe not … it seems like somethings up. Here’s how I run a request:

(defn execute-request
  [routes path request]
  (let [interceptors (-> routes
                         (table/table-routes)
                         (route/router :prefix-tree))]
    (-> {:request (merge {:request-method :get}
                         {:path-info path}
                         request)}
        (chain/enqueue [interceptors])
        chain/execute)))

hlship22:08:09

This is part of my test suite. This is returning as expected for requests that don’t involve any async, but returns nil for those that are async.

hlship22:08:31

This might be related: https://github.com/pedestal/pedestal/issues/430 I need to dig through more of the code to see what “goes async” really means.

hlship23:08:47

My guess is that the stack of interceptors built up in execute-request doesn’t reflect usage in a real server; I’m working on end-to-end tests instead.

hlship23:08:00

End to end worked perfectly so it’s just a matter of my test harness not being up to snuff. Some docs or examples on how to set up the interceptor chain for fast testing in this way would be welcome!