diff --git a/src/gorilla_repl/core.clj b/src/gorilla_repl/core.clj index 38057d3f6..98e307af7 100644 --- a/src/gorilla_repl/core.clj +++ b/src/gorilla_repl/core.clj @@ -22,9 +22,10 @@ (POST "/save" [] (handle/wrap-api-handler handle/save)) (GET "/gorilla-files" [] (handle/wrap-api-handler handle/gorilla-files)) (GET "/config" [] (handle/wrap-api-handler handle/config)) - (GET "/repl" [] ws-relay/ring-handler) - (route/resources "/" {:root "gorilla-repl-client"}) - (route/files "/project-files" {:root "."})) + ;; (GET "/repl" [] (ws-relay/repl-ring-handler ws-relay/on-receive-mem)) + (GET "/repl" [] (ws-relay/repl-ring-handler ws-relay/on-receive-net)) + (route/resources "/") + (route/files "/project-files" [:root "."])) (defn run-gorilla-server diff --git a/src/gorilla_repl/websocket_relay.clj b/src/gorilla_repl/websocket_relay.clj index be06477d8..8466bf79e 100644 --- a/src/gorilla_repl/websocket_relay.clj +++ b/src/gorilla_repl/websocket_relay.clj @@ -6,7 +6,13 @@ (ns gorilla-repl.websocket-relay (:require [org.httpkit.server :as server] + [clojure.tools.nrepl.server :as nrepl-server] [clojure.tools.nrepl :as nrepl] + [clojure.tools.nrepl [transport :as transport]] + [gorilla-repl.render-values-mw :as render-mw] ;; it's essential this import comes after the previous one! + [cider.nrepl :as cider] + [ring.middleware.session :as session] + [ring.middleware.session.memory :as mem] [cheshire.core :as json])) ;; We will open a single connection to the nREPL server for the life of the application. It will be stored here. @@ -20,26 +26,69 @@ (let [new-conn (nrepl/connect :port port)] (swap! conn (fn [x] new-conn)))) -(defn- send-json-over-ws - [channel data] - (let [json-data (json/generate-string data)] - #_(println json-data) - (server/send! channel json-data))) +(def ^:private nrepl-handler (apply nrepl-server/default-handler + (-> (map resolve cider/cider-middleware) + (conj #'render-mw/render-values)))) + +(defn- process-replies + [reply-fn replies] + (doall (->> replies + (map reply-fn)))) -(defn- process-message +(defn- process-message-net [channel data] (let [parsed-message (assoc (json/parse-string data true) :as-html 1) client (nrepl/client @conn Long/MAX_VALUE) replies (nrepl/message client parsed-message)] ;; send the messages out over the WS connection one-by-one. - (doall (map (partial send-json-over-ws channel) replies)))) - -(defn ring-handler - "This ring handler expects the client to make a websocket connection to the endpoint. It relays messages back and - forth to an nREPL server. A connection to the nREPL server must have previously been made with 'connect-to-nrepl'. - Messages are mapped back and forth to JSON as they pass through the relay." - [request] - (server/with-channel - request - channel - (server/on-receive channel (partial process-message channel)))) \ No newline at end of file + (let [reply-fn (partial process-replies + #(server/send! + channel + {:body (json/generate-string %)}))] + (reply-fn replies)))) + +(defn- process-message-mem + [transport channel timeout data] + (let [msg (assoc (json/parse-string data true) :as-html 1) + [read write] transport + client (nrepl/client read timeout)] + ((partial process-replies #(server/send! + channel + {:body (json/generate-string %) + :session {::tranport transport}})) + (do + (when (:op msg) + (future (nrepl-server/handle* msg nrepl-handler write))) + (client))))) + +(defn- memory-session + "Wraps the supplied handler in session middleware that uses a + private memory store." + [handler] + (let [store (mem/memory-store)] + (session/wrap-session handler {:store store :cookie-name "gorilla-session"}))) + + +(defn on-receive-net + "Relays messages back and forth to an nREPL server. A connection to the nREPL server must have + previously been made with 'connect-to-nrepl'." + [_ channel] + (partial process-message-net channel)) + +(defn on-receive-mem + "Passes messages into nREPL (in memory)" + [request channel] + (let [session (:session request) + transport (or (::transport session) + (transport/piped-transports))] + (partial process-message-mem transport channel 1000))) + +(defn repl-ring-handler + "Creates a websocket ring handler for nrepl messages. Messages are mapped back and forth to JSON." + [on-receive-fn] + (-> (fn [request] + (server/with-channel + request + channel + (server/on-receive channel (on-receive-fn request channel)))) + (memory-session))) \ No newline at end of file