|
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 | + [org.eclipse.jetty.websocket.server.config |
| 31 | + JettyWebSocketServletContainerInitializer] |
19 | 32 | [jakarta.servlet AsyncContext DispatcherType AsyncEvent AsyncListener]
|
20 | 33 | [jakarta.servlet.http HttpServletRequest HttpServletResponse]))
|
21 | 34 |
|
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)))))) |
| 35 | +(defn- websocket-socket [^Session session] |
| 36 | + (let [remote (.getRemote session)] |
| 37 | + (reify ws/Socket |
| 38 | + (-send [_ message] |
| 39 | + (if (string? message) |
| 40 | + (.sendString remote message) |
| 41 | + (.sendBytes remote message))) |
| 42 | + (-ping [_ data] |
| 43 | + (.sendPing remote data)) |
| 44 | + (-pong [_ data] |
| 45 | + (.sendPong remote data)) |
| 46 | + (-close [_ status reason] |
| 47 | + (.close session status reason))))) |
| 48 | + |
| 49 | +(defn- websocket-listener [listener] |
| 50 | + (let [socket (volatile! nil)] |
| 51 | + (reify |
| 52 | + WebSocketConnectionListener |
| 53 | + (onWebSocketConnect [_ session] |
| 54 | + (vreset! socket (websocket-socket session)) |
| 55 | + (ws/on-open listener @socket)) |
| 56 | + (onWebSocketClose [_ status reason] |
| 57 | + (ws/on-close listener @socket status reason)) |
| 58 | + (onWebSocketError [_ throwable] |
| 59 | + (ws/on-error listener @socket throwable)) |
| 60 | + WebSocketListener |
| 61 | + (onWebSocketText [_ message] |
| 62 | + (ws/on-message listener @socket message)) |
| 63 | + (onWebSocketBinary [_ payload offset length] |
| 64 | + (let [buffer (ByteBuffer/wrap payload offset length)] |
| 65 | + (ws/on-message listener @socket buffer))) |
| 66 | + WebSocketPingPongListener |
| 67 | + (onWebSocketPing [_ _]) |
| 68 | + (onWebSocketPong [_ payload] |
| 69 | + (ws/on-pong listener @socket payload))))) |
| 70 | + |
| 71 | +(defn- websocket-creator [{listener ::ws/listener}] |
| 72 | + (reify JettyWebSocketCreator |
| 73 | + (createWebSocket [_ _ _] |
| 74 | + (websocket-listener listener)))) |
| 75 | + |
| 76 | +(defn- upgrade-to-websocket [^HttpServletRequest request response response-map] |
| 77 | + (let [context (.getServletContext request) |
| 78 | + container (JettyWebSocketServerContainer/getContainer context) |
| 79 | + creator (websocket-creator response-map)] |
| 80 | + (.upgrade container creator request response))) |
| 81 | + |
| 82 | +(defn- ^ServletHandler proxy-handler [handler] |
| 83 | + (proxy [ServletHandler] [] |
| 84 | + (doHandle [_ ^Request base-request request response] |
| 85 | + (let [request-map (servlet/build-request-map request) |
| 86 | + response-map (handler request-map)] |
| 87 | + (try |
| 88 | + (if (ws/websocket-response? response-map) |
| 89 | + (upgrade-to-websocket request response response-map) |
| 90 | + (servlet/update-servlet-response response response-map)) |
| 91 | + (finally |
| 92 | + (.setHandled base-request true))))))) |
30 | 93 |
|
31 | 94 | (defn- async-jetty-raise [^AsyncContext context ^HttpServletResponse response]
|
32 | 95 | (fn [^Throwable exception]
|
33 | 96 | (.sendError response 500 (.getMessage exception))
|
34 | 97 | (.complete context)))
|
35 | 98 |
|
36 |
| -(defn- async-jetty-respond [context response] |
| 99 | +(defn- async-jetty-respond [context request response] |
37 | 100 | (fn [response-map]
|
38 |
| - (servlet/update-servlet-response response context response-map))) |
| 101 | + (if (ws/websocket-response? response-map) |
| 102 | + (upgrade-to-websocket request response response-map) |
| 103 | + (servlet/update-servlet-response response context response-map)))) |
39 | 104 |
|
40 | 105 | (defn- async-timeout-listener [request context response handler]
|
41 | 106 | (proxy [AsyncListener] []
|
42 | 107 | (onTimeout [^AsyncEvent _]
|
43 | 108 | (handler (servlet/build-request-map request)
|
44 |
| - (async-jetty-respond context response) |
| 109 | + (async-jetty-respond context request response) |
45 | 110 | (async-jetty-raise context response)))
|
46 | 111 | (onComplete [^AsyncEvent _])
|
47 | 112 | (onError [^AsyncEvent _])
|
48 | 113 | (onStartAsync [^AsyncEvent _])))
|
49 | 114 |
|
50 |
| -(defn- ^AbstractHandler async-proxy-handler [handler timeout timeout-handler] |
51 |
| - (proxy [AbstractHandler] [] |
52 |
| - (handle [_ ^Request base-request ^HttpServletRequest request ^HttpServletResponse response] |
| 115 | +(defn- ^ServletHandler async-proxy-handler [handler timeout timeout-handler] |
| 116 | + (proxy [ServletHandler] [] |
| 117 | + (doHandle [_ ^Request base-request request response] |
53 | 118 | (let [^AsyncContext context (.startAsync request)]
|
54 | 119 | (.setTimeout context timeout)
|
55 | 120 | (when timeout-handler
|
56 | 121 | (.addListener
|
57 | 122 | context
|
58 | 123 | (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))))) |
| 124 | + (try |
| 125 | + (handler |
| 126 | + (servlet/build-request-map request) |
| 127 | + (async-jetty-respond context request response) |
| 128 | + (async-jetty-raise context response)) |
| 129 | + (finally |
| 130 | + (.setHandled base-request true))))))) |
| 131 | + |
| 132 | +(defn- ^ServletContextHandler context-handler [proxy-handler] |
| 133 | + (doto (ServletContextHandler.) |
| 134 | + (.setServletHandler proxy-handler) |
| 135 | + (.setAllowNullPathInfo true) |
| 136 | + (JettyWebSocketServletContainerInitializer/configure nil))) |
64 | 137 |
|
65 | 138 | (defn- ^ServerConnector server-connector [^Server server & factories]
|
66 | 139 | (ServerConnector. server #^"[Lorg.eclipse.jetty.server.ConnectionFactory;" (into-array ConnectionFactory factories)))
|
|
213 | 286 | :response-header-size - the maximum size of a response header (default 8192)
|
214 | 287 | :send-server-version? - add Server header to HTTP response (default true)"
|
215 | 288 | [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))) |
| 289 | + (let [server (create-server (dissoc options :configurator)) |
| 290 | + proxy (if (:async? options) |
| 291 | + (async-proxy-handler handler |
| 292 | + (:async-timeout options 0) |
| 293 | + (:async-timeout-handler options)) |
| 294 | + (proxy-handler handler))] |
| 295 | + (.setHandler server (context-handler proxy)) |
223 | 296 | (when-let [configurator (:configurator options)]
|
224 | 297 | (configurator server))
|
225 | 298 | (try
|
|
0 commit comments