Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions src/gorilla_repl/core.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
83 changes: 66 additions & 17 deletions src/gorilla_repl/websocket_relay.clj
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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))))
(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)))