Fork me on GitHub
#clojure
<
2024-05-06
>
stopa18:05:46

Question for folks who have experience with undertow: Problem: If I manually close a channel, I notice that neither on-close-message or on-error callbacks are called. For example:

(def id-atom (atom 0))
(defn ws-handler [_]
  (let [id (swap! id-atom inc)]
    {:undertow/websocket
     {:on-open (fn [{:keys [channel]}]
                 (println (format " [%s] online!" id))
                 (ws/send (format " [%s] ok" id) channel))
      :on-message (fn [{:keys [channel data]}]
                    (if (= "break" (str/trim data))
                      (do
                        (println (format " [%s] about to break!" id))
                        (.close channel))
                      (do
                        (println (format " [%s] received %s" id data))
                        (ws/send (format " [%s] received %s" id data) channel))))

      :on-close-message (fn [_]
                          (println (format " [%s] closed!" id)))

      :on-error (fn [_]
                  (println (format " [%s] error! " id)))}})) 
If I send the break command in this websocket, the client will disconnect, but on-close-message or on-error will never get called. Is this expected? Previously I thought I could rely on on-close-message being called, so I could always do some kind of cleanup in that callback. But writing this example makes me a bit confused: are there times when websocket connections can close, but the callback won't run? I made a quick repro project here: https://github.com/stopachka/manual-close-error/tree/main Thoughts much appreciated!

hiredman18:05:05

don't call .close on the channel, that likely closes the tcp connection and doesn't cleanly shutdown the websocket connection

hiredman18:05:16

part of the websocket protocol is there is an explicit close message that can contain an information number code about why the socket is being closed

hiredman18:05:48

on-close-message handles those messages, calling .close on the channel likely closes at least the server sending side of the tcp connection (maybe both sides?), and the client might respond to that by deciding the websocket is broken and not bothering to do a clean shutdown of the connection

hiredman19:05:02

you can see here(https://github.com/ring-clojure/ring/blob/master/ring-websocket-protocols/src/ring/websocket/protocols.clj#L40) the relatively new ring spec for websockets includes a close function that takes the reason code for sending to the client, but it looks like the non-standard websocket extension the luminus undertow ring adapter provides doesn't seem to have such a thing