pedestal

emccue 2020-06-12T02:29:24.194400Z

How would i add an interceptor to "everything"

hlship 2020-06-12T16:31:10.198700Z

When building your service, you can specify interceptors that come before routing; these affect everything.

hlship 2020-06-12T16:31:54.198900Z

Disadvantage: may do work that is unnecessary, especially in case where incoming request can not be routed.

emccue 2020-06-12T02:30:27.194900Z

I'm thinking it makes sense to have my db pool as context accessible from an http request

2020-06-12T11:11:59.198500Z

@emccue if you mean having an interceptor execute prior to any handler irrespective of route then you can create a common-interceptors coll and add it there. The buddy-auth sample does this to ensure the authentication interceptors are executed for each route (https://github.com/pedestal/pedestal/blob/master/samples/buddy-auth/src/buddy_auth/service.clj#L74-L80)

isak 2020-06-12T17:09:48.199500Z

How do I know which websocket session sent the message here? https://github.com/pedestal/pedestal/blob/master/samples/jetty-web-sockets/src/jetty_web_sockets/service.clj#L58

isak 2020-06-12T17:10:07.199900Z

Seems like the callback only gets the message. Is there a way to change it?

isak 2020-06-12T17:21:41.201100Z

thanks, I was just about to make my own listener

2020-06-12T17:22:58.201700Z

i was trying to find the slack logs, but looks like it's down now

2020-06-12T17:24:14.202400Z

this was the sample listener

(defn ws-listener
  [_request _response ws-map]
  (proxy [WebSocketAdapter] []
    (onWebSocketConnect [^Session ws-session]
      (proxy-super onWebSocketConnect ws-session)
      (when-let [f (:on-connect ws-map)]
        (f ws-session)))
    (onWebSocketClose [status-code reason]
      (when-let [f (:on-close ws-map)]
        (f (.getSession this) status-code reason)))
    (onWebSocketError [^Throwable e]
      (when-let [f (:on-error ws-map)]
        (f (.getSession this) e)))
    (onWebSocketText [^String message]
      (when-let [f (:on-text ws-map)]
        (f (.getSession this) message)))
    (onWebSocketBinary [^bytes payload offset length]
      (when-let [f (:on-binary ws-map)]
        (f (.getSession this) payload offset length)))))

      ;; in your service map:
::http/container-options {:context-configurator #(ws/add-ws-endpoints % ws-paths {:listener-fn ws-listener})}

isak 2020-06-12T17:25:27.202900Z

nice, thanks @danvingo

2020-06-12T17:27:19.203700Z

no prob, I've been meaning to try this out too. would be great if you don't mind posting your working service setup when you have one

isak 2020-06-12T17:27:47.203900Z

ok

hindol 2020-06-12T17:57:04.204800Z

@isak I hacked together the above sample, inspired by https://github.com/sunng87/ring-jetty9-adapter/blob/40aab6d0b91a7d7b63b9bf39e62e1f9801a02d3e/src/ring/adapter/jetty9/websocket.clj#L122 Hit me up if you cannot get it to work.

isak 2020-06-12T18:00:09.206400Z

cool @hindol.adhya @danvingo, I just got it to work. Btw I noticed it is pretty hard to get a good reloading experience with the socket handlers, but here is one thing that works:

isak 2020-06-12T18:01:31.207700Z

(I tried putting the var at a higher level, but didn't work)

hindol 2020-06-12T18:01:44.208100Z

Yeah Java code is calling into this code. Reloading should work with the right indirection in place.

lucian303 2020-06-12T18:02:09.208200Z

i'm writing a 'not found' interceptor to send all requests that were not fulfilled to another backend server and passes through its response to the output. all works, except for requests w/ a really large body, the body gets cut off in the output. it's complete in the request though. when i println the request i can see the entire body. but the output is truncated. i'm using immutant though jetty does the same. what could be causing this?

(defn send-request
  [request]
  (println request)
  (try
    (let [params   {:url                (str (config/env :neo-api-url) (:uri request))
                    :body               (:body request)
                    :method             (:request-method request)
                    :socket-timeout     10000 ;; in milliseconds
                    :connection-timeout 1000
                    :headers            (merge (:headers request)
                                               {:authorization (str "Bearer " (config/env :neo-api-token))
                                                :accept        "application/vnd.ucf.v1+json"})}
          response (client/request params)]
      (println response)
      response)
    (catch Exception e
      (timbre/warn "Could not reach API server" e))))

(def passthrough
  {:name  ::passthrough
   :leave (fn [context]
            (let [request (:request context)]
              (assoc context :response (send-request request))))})

isak 2020-06-12T18:02:16.208400Z

right

hindol 2020-06-12T18:07:30.208500Z

Hi, I have created a Gist with the code sample. If you find a clean way to make reloaded workflow work, please add here. https://gist.github.com/Hindol/854ed24594e2909abfae7d45cd8cf391

isak 2020-06-12T18:19:34.208900Z

Couldn't find an ideal way, but I'll add what I have

hindol 2020-06-12T18:23:39.209100Z

I have a hunch update-proxy will come handy here. https://clojuredocs.org/clojure.core/update-proxy There was something in The Joy of Clojure book.

lucian303 2020-06-12T18:23:42.209300Z

i think the issue is the headers coming back esp content-length. i updated the code to only take the body and put it in a new response and it's working now:

(assoc context :response (response/response (:body (send-request request))))))})

2020-06-12T18:31:16.209400Z

thanks to both of you!

isak 2020-06-12T18:32:15.209600Z

oh good point hindol, let me try

isak 2020-06-12T19:08:40.209800Z

Think I'm close, but I don't know how to properly listen to when a variable changes - i tried add-watch but that did not work

hindol 2020-06-12T19:18:06.210Z

Does manually calling update-proxy work? I feel adding a watch is going too far.

isak 2020-06-12T19:48:27.210200Z

alright I got it, but I guess you won't like it

isak 2020-06-12T19:48:33.210400Z

(defn add-ws-endpoints
  "Given a ServletContextHandler and a map of WebSocket (String) paths to action maps,
  produce corresponding Servlets per path and add them to the context.
  Return the context when complete.

  You may optionally also pass in a map of options.
  Currently supported options:
   :listener-fn - A function of 3 args,
                  the ServletUpgradeRequest, ServletUpgradeResponse, and the WS-Map
                  that returns a WebSocketListener."
  ([^ServletContextHandler ctx ws-paths]
   (add-ws-endpoints ctx ws-paths {:listener-fn (fn [req response ws-map]
                                                  (ws/make-ws-listener ws-map))}))
  ([^ServletContextHandler ctx ws-paths opts]
   (let [{:keys [listener-fn]
          :or {listener-fn (fn [req response ws-map]
                             (ws/make-ws-listener ws-map))}} opts
         ws-paths-orig ws-paths
         ws-paths (cond-> ws-paths var? deref)]
     (doseq [[path ws-map] ws-paths]
       (let [servlet (ws/ws-servlet
                       (fn [req response]
                         (let [ws-paths (cond-> ws-paths-orig var? deref)]
                           (listener-fn req response (get ws-paths path)))))]
         (.addServlet ctx (ServletHolder. ^javax.servlet.Servlet servlet) path)))
     ctx)))

(def WebSocketAdapterProxy (get-proxy-class WebSocketAdapter))

(defn ws-listener
  [_request _response ws-map]
  (let [get-proxy-members
                (fn [ws-map]
                  {"onWebSocketConnect"
                   (fn [this ^Session ws-session]
                     (proxy-super onWebSocketConnect ws-session)
                     (when-let [f (:on-connect ws-map)]
                       (f ws-session)))
                   "onWebSocketClose"
                   (fn [this status-code reason]
                     (when (var? ws-map) 
                       (remove-watch ws-map :ws-map-watcher))
                     (when-let [f (:on-close ws-map)]
                       (f (.getSession this) status-code reason)))
                   "onWebSocketError"
                   (fn [this ^Throwable e]
                     (when-let [f (:on-error ws-map)]
                       (f (.getSession this) e)))

                   "onWebSocketText"
                   (fn [this ^String message]
                     (when-let [f (:on-text ws-map)]
                       (f (.getSession this) message)))

                   "onWebSocketBinary"
                   (fn [this ^bytes payload offset length]
                     (when-let [f (:on-binary ws-map)]
                       (f (.getSession this) payload offset length)))})
        adapter (construct-proxy WebSocketAdapterProxy)]
    (init-proxy adapter (get-proxy-members ws-map))
    (when (var? ws-map)
      (add-watch
        ws-map
        :ws-map-watcher
        (fn [_ _ _ ws-map]
          (update-proxy adapter (get-proxy-members ws-map)))))
    adapter))

isak 2020-06-12T19:51:19.210600Z

I guess a weakness is you cannot add new websocket paths at runtime (e.g., ["ws"] -> ["ws", "ws2"]), but I think that is more rare

hindol 2020-06-12T20:00:31.210800Z

That's a cool solution. If you are willing to go so far, I wonder if making ws-map an atom will be better. That way the proxy will "see" the latest defs without needing a watcher and update proxy.

isak 2020-06-12T20:31:45.211100Z

Yea that would be simpler, though then the distance between the dev version and the prod version would be a little greater. So I just wanted to do it like how pedestal people normally treat the routes in dev vs prod.

👍 1
hindol 2020-06-12T20:43:14.211300Z

Yeah, what you said makes sense actually.

1
lucian303 2020-06-12T20:51:29.213300Z

is there an interceptor or other way to define params using plumatic schema like compojure-api/reitit or some other way of declarative parameter handling? other than using the reitit router in pedestal that is

2020-06-14T12:14:26.216300Z

Hi, have you looked at https://github.com/oliyh/pedestal-api ?

lucian303 2020-06-15T17:12:13.225800Z

i have briefly. wasn't sure if it was still an active project. same thing with the rook project. i will take a closer look

2020-06-16T07:44:27.226500Z

It's stable as in no outstanding bugs and no outstanding features. If you think it's missing anything or you find a bug just raise an issue/pr