From a9b7874f368ebf3520efc4cf27e37a8c119b0990 Mon Sep 17 00:00:00 2001 From: Per Magnus Petersen Date: Fri, 6 Mar 2026 07:50:53 +0100 Subject: [PATCH] Use idiomatic undertow/websocket maps instead of raw WebSocket API Co-Authored-By: Claude Opus 4.6 --- .../pmagnus/btcprice/web/middleware/core.clj | 5 +-- .../pmagnus/btcprice/web/routes/ws_proxy.clj | 33 ++++++++----------- 2 files changed, 16 insertions(+), 22 deletions(-) diff --git a/src/clj/pmagnus/btcprice/web/middleware/core.clj b/src/clj/pmagnus/btcprice/web/middleware/core.clj index b420c43..f2578a6 100644 --- a/src/clj/pmagnus/btcprice/web/middleware/core.clj +++ b/src/clj/pmagnus/btcprice/web/middleware/core.clj @@ -4,11 +4,12 @@ (defn- wrap-nil-guard "Discard responses with no :status — these are artifacts of ring-defaults - middleware wrapping a nil response (e.g., from a raw WebSocket upgrade)." + middleware wrapping a nil response (e.g., from a raw WebSocket upgrade). + Passes through :undertow/websocket responses for the adapter to handle." [handler] (fn [request] (let [resp (handler request)] - (when (:status resp) resp)))) + (when (or (:status resp) (:undertow/websocket resp)) resp)))) (defn wrap-base [{:keys [site-defaults-config]}] (fn [handler] diff --git a/src/clj/pmagnus/btcprice/web/routes/ws_proxy.clj b/src/clj/pmagnus/btcprice/web/routes/ws_proxy.clj index f0d1ad9..29f29c6 100644 --- a/src/clj/pmagnus/btcprice/web/routes/ws_proxy.clj +++ b/src/clj/pmagnus/btcprice/web/routes/ws_proxy.clj @@ -3,9 +3,7 @@ [clojure.tools.logging :as log] [integrant.core :as ig]) (:import - [io.undertow.server HttpServerExchange] - [io.undertow.websockets WebSocketConnectionCallback WebSocketProtocolHandshakeHandler] - [io.undertow.websockets.core AbstractReceiveListener WebSockets WebSocketChannel] + [io.undertow.websockets.core WebSockets WebSocketChannel] [java.net URI] [java.net.http HttpClient WebSocket$Builder WebSocket$Listener] [java.util.concurrent CompletableFuture])) @@ -47,24 +45,19 @@ (.buildAsync (URI. btcdata-ws-url) listener) (.join)))) -(defn- ws-proxy-handler [{:keys [btcdata-url]} req] - (let [^HttpServerExchange exchange (:server-exchange req) - btcdata-ws-url (str (.replaceFirst ^String btcdata-url "^http" "ws") +(defn- ws-proxy-handler [{:keys [btcdata-url]} _req] + (let [btcdata-ws-url (str (.replaceFirst ^String btcdata-url "^http" "ws") "/api/price/ws") - callback (proxy [WebSocketConnectionCallback] [] - (onConnect [_ws-exchange browser-ch] - (log/info "Browser connected, proxying to" btcdata-ws-url) - (let [upstream (connect-upstream btcdata-ws-url browser-ch)] - ;; Listen for browser close to tear down upstream - (.set (.getReceiveSetter ^WebSocketChannel browser-ch) - (proxy [AbstractReceiveListener] [] - (onCloseMessage [_this _msg _ch] - (log/debug "Browser closed") - (close-upstream-quietly upstream)))) - (.resumeReceives ^WebSocketChannel browser-ch)))) - handler (WebSocketProtocolHandshakeHandler. callback)] - (.handleRequest handler exchange) - nil)) + upstream-atom (atom nil)] + {:undertow/websocket + {:on-open + (fn [{:keys [^WebSocketChannel channel]}] + (log/info "Browser connected, proxying to" btcdata-ws-url) + (reset! upstream-atom (connect-upstream btcdata-ws-url channel))) + :on-close-message + (fn [_] + (log/debug "Browser closed") + (close-upstream-quietly @upstream-atom))}})) (defn- ws-proxy-routes [opts] [["/ws/price"