|
2 | 2 | "A Ring adapter that uses the Jetty 9 embedded web server. |
3 | 3 |
|
4 | 4 | Adapters are used to convert Ring handlers into running web servers." |
5 | | - (:require [ring.util.jakarta.servlet :as servlet]) |
6 | | - (:import [org.eclipse.jetty.server |
| 5 | + (:require [ring.util.jakarta.servlet :as servlet] |
| 6 | + [ring.websocket :as ws]) |
| 7 | + (:import [java.nio ByteBuffer] |
| 8 | + [org.eclipse.jetty.server |
7 | 9 | Request |
8 | 10 | Server |
9 | 11 | ServerConnector |
|
12 | 14 | HttpConnectionFactory |
13 | 15 | SslConnectionFactory |
14 | 16 | SecureRequestCustomizer] |
| 17 | + [org.eclipse.jetty.servlet ServletContextHandler ServletHandler] |
15 | 18 | [org.eclipse.jetty.server.handler AbstractHandler] |
16 | 19 | [org.eclipse.jetty.util BlockingArrayQueue] |
17 | 20 | [org.eclipse.jetty.util.thread ThreadPool QueuedThreadPool] |
18 | 21 | [org.eclipse.jetty.util.ssl SslContextFactory$Server KeyStoreScanner] |
| 22 | + [org.eclipse.jetty.websocket.server |
| 23 | + JettyWebSocketServerContainer |
| 24 | + JettyWebSocketCreator] |
| 25 | + [org.eclipse.jetty.websocket.api |
| 26 | + Session |
| 27 | + WebSocketConnectionListener |
| 28 | + WebSocketListener |
| 29 | + WebSocketPingPongListener |
| 30 | + WriteCallback] |
| 31 | + [org.eclipse.jetty.websocket.server.config |
| 32 | + JettyWebSocketServletContainerInitializer] |
19 | 33 | [jakarta.servlet AsyncContext DispatcherType AsyncEvent AsyncListener] |
20 | 34 | [jakarta.servlet.http HttpServletRequest HttpServletResponse])) |
21 | 35 |
|
22 | | -(defn- ^AbstractHandler proxy-handler [handler] |
23 | | - (proxy [AbstractHandler] [] |
24 | | - (handle [_ ^Request base-request ^HttpServletRequest request response] |
25 | | - (when-not (= (.getDispatcherType request) DispatcherType/ERROR) |
26 | | - (let [request-map (servlet/build-request-map request) |
27 | | - response-map (handler request-map)] |
28 | | - (servlet/update-servlet-response response response-map) |
29 | | - (.setHandled base-request true)))))) |
| 36 | +(defn- websocket-socket [^Session session] |
| 37 | + (let [remote (.getRemote session)] |
| 38 | + (reify |
| 39 | + ws/Socket |
| 40 | + (-send [_ message] |
| 41 | + (if (string? message) |
| 42 | + (.sendString remote message) |
| 43 | + (.sendBytes remote message))) |
| 44 | + (-ping [_ data] |
| 45 | + (.sendPing remote data)) |
| 46 | + (-pong [_ data] |
| 47 | + (.sendPong remote data)) |
| 48 | + (-close [_ status reason] |
| 49 | + (.close session status reason)) |
| 50 | + ws/AsyncSocket |
| 51 | + (-send-async [_ message succeed fail] |
| 52 | + (let [callback (reify WriteCallback |
| 53 | + (writeSuccess [_] (succeed)) |
| 54 | + (writeFailed [_ ex] (fail ex)))] |
| 55 | + (if (string? message) |
| 56 | + (.sendString remote message callback) |
| 57 | + (.sendBytes remote message callback))))))) |
| 58 | + |
| 59 | +(defn- websocket-listener [listener] |
| 60 | + (let [socket (volatile! nil)] |
| 61 | + (reify |
| 62 | + WebSocketConnectionListener |
| 63 | + (onWebSocketConnect [_ session] |
| 64 | + (vreset! socket (websocket-socket session)) |
| 65 | + (ws/on-open listener @socket)) |
| 66 | + (onWebSocketClose [_ status reason] |
| 67 | + (ws/on-close listener @socket status reason)) |
| 68 | + (onWebSocketError [_ throwable] |
| 69 | + (ws/on-error listener @socket throwable)) |
| 70 | + WebSocketListener |
| 71 | + (onWebSocketText [_ message] |
| 72 | + (ws/on-message listener @socket message)) |
| 73 | + (onWebSocketBinary [_ payload offset length] |
| 74 | + (let [buffer (ByteBuffer/wrap payload offset length)] |
| 75 | + (ws/on-message listener @socket buffer))) |
| 76 | + WebSocketPingPongListener |
| 77 | + (onWebSocketPing [_ _]) |
| 78 | + (onWebSocketPong [_ payload] |
| 79 | + (ws/on-pong listener @socket payload))))) |
| 80 | + |
| 81 | +(defn- websocket-creator [{listener ::ws/listener}] |
| 82 | + (reify JettyWebSocketCreator |
| 83 | + (createWebSocket [_ _ _] |
| 84 | + (websocket-listener listener)))) |
| 85 | + |
| 86 | +(defn- upgrade-to-websocket [^HttpServletRequest request response response-map] |
| 87 | + (let [context (.getServletContext request) |
| 88 | + container (JettyWebSocketServerContainer/getContainer context) |
| 89 | + creator (websocket-creator response-map)] |
| 90 | + (.upgrade container creator request response))) |
| 91 | + |
| 92 | +(defn- ^ServletHandler proxy-handler [handler] |
| 93 | + (proxy [ServletHandler] [] |
| 94 | + (doHandle [_ ^Request base-request request response] |
| 95 | + (let [request-map (servlet/build-request-map request) |
| 96 | + response-map (handler request-map)] |
| 97 | + (try |
| 98 | + (if (ws/websocket-response? response-map) |
| 99 | + (upgrade-to-websocket request response response-map) |
| 100 | + (servlet/update-servlet-response response response-map)) |
| 101 | + (finally |
| 102 | + (.setHandled base-request true))))))) |
30 | 103 |
|
31 | 104 | (defn- async-jetty-raise [^AsyncContext context ^HttpServletResponse response] |
32 | 105 | (fn [^Throwable exception] |
33 | 106 | (.sendError response 500 (.getMessage exception)) |
34 | 107 | (.complete context))) |
35 | 108 |
|
36 | | -(defn- async-jetty-respond [context response] |
| 109 | +(defn- async-jetty-respond [context request response] |
37 | 110 | (fn [response-map] |
38 | | - (servlet/update-servlet-response response context response-map))) |
| 111 | + (if (ws/websocket-response? response-map) |
| 112 | + (upgrade-to-websocket request response response-map) |
| 113 | + (servlet/update-servlet-response response context response-map)))) |
39 | 114 |
|
40 | 115 | (defn- async-timeout-listener [request context response handler] |
41 | 116 | (proxy [AsyncListener] [] |
42 | 117 | (onTimeout [^AsyncEvent _] |
43 | 118 | (handler (servlet/build-request-map request) |
44 | | - (async-jetty-respond context response) |
| 119 | + (async-jetty-respond context request response) |
45 | 120 | (async-jetty-raise context response))) |
46 | 121 | (onComplete [^AsyncEvent _]) |
47 | 122 | (onError [^AsyncEvent _]) |
48 | 123 | (onStartAsync [^AsyncEvent _]))) |
49 | 124 |
|
50 | | -(defn- ^AbstractHandler async-proxy-handler [handler timeout timeout-handler] |
51 | | - (proxy [AbstractHandler] [] |
52 | | - (handle [_ ^Request base-request ^HttpServletRequest request ^HttpServletResponse response] |
| 125 | +(defn- ^ServletHandler async-proxy-handler [handler timeout timeout-handler] |
| 126 | + (proxy [ServletHandler] [] |
| 127 | + (doHandle [_ ^Request base-request request response] |
53 | 128 | (let [^AsyncContext context (.startAsync request)] |
54 | 129 | (.setTimeout context timeout) |
55 | 130 | (when timeout-handler |
56 | 131 | (.addListener |
57 | 132 | context |
58 | 133 | (async-timeout-listener request context response timeout-handler))) |
59 | | - (handler |
60 | | - (servlet/build-request-map request) |
61 | | - (async-jetty-respond context response) |
62 | | - (async-jetty-raise context response)) |
63 | | - (.setHandled base-request true))))) |
| 134 | + (try |
| 135 | + (handler |
| 136 | + (servlet/build-request-map request) |
| 137 | + (async-jetty-respond context request response) |
| 138 | + (async-jetty-raise context response)) |
| 139 | + (finally |
| 140 | + (.setHandled base-request true))))))) |
| 141 | + |
| 142 | +(defn- ^ServletContextHandler context-handler [proxy-handler] |
| 143 | + (doto (ServletContextHandler.) |
| 144 | + (.setServletHandler proxy-handler) |
| 145 | + (.setAllowNullPathInfo true) |
| 146 | + (JettyWebSocketServletContainerInitializer/configure nil))) |
64 | 147 |
|
65 | 148 | (defn- ^ServerConnector server-connector [^Server server & factories] |
66 | 149 | (ServerConnector. server #^"[Lorg.eclipse.jetty.server.ConnectionFactory;" (into-array ConnectionFactory factories))) |
|
213 | 296 | :response-header-size - the maximum size of a response header (default 8192) |
214 | 297 | :send-server-version? - add Server header to HTTP response (default true)" |
215 | 298 | [handler options] |
216 | | - (let [server (create-server (dissoc options :configurator))] |
217 | | - (if (:async? options) |
218 | | - (.setHandler server |
219 | | - (async-proxy-handler handler |
220 | | - (:async-timeout options 0) |
221 | | - (:async-timeout-handler options))) |
222 | | - (.setHandler server (proxy-handler handler))) |
| 299 | + (let [server (create-server (dissoc options :configurator)) |
| 300 | + proxy (if (:async? options) |
| 301 | + (async-proxy-handler handler |
| 302 | + (:async-timeout options 0) |
| 303 | + (:async-timeout-handler options)) |
| 304 | + (proxy-handler handler))] |
| 305 | + (.setHandler server (context-handler proxy)) |
223 | 306 | (when-let [configurator (:configurator options)] |
224 | 307 | (configurator server)) |
225 | 308 | (try |
|
0 commit comments