summaryrefslogtreecommitdiff
path: root/vim/bundle/slimv/swank-clojure/swank/core/server.clj
blob: 1c9f70a458082ec3fc639e95fe9f69a0c718c72d (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
(ns swank.core.server
  (:use (swank util core)
        (swank.util sys io)
        (swank.util.concurrent thread)
        (swank.util.net sockets)
        (swank.core connection protocol))
  (:import (java.io File FileReader BufferedReader InputStreamReader OutputStreamWriter)
           (java.net Socket)))

;; The swank.core.server is the layer above swank.util.net.sockets
;;  - Manages the socket server
;;  - Accepts and authenticates incoming connections
;;  - Creates swank.core.connections
;;  - Spins up new threads

(defonce connections (ref []))

(def slime-secret-path (str (user-home-path) File/separator ".slime-secret"))

(defn- slime-secret
  "Returns the first line from the slime-secret file, path found in
   slime-secret-path (default: .slime-secret in the user's home
   directory).

   See also: `accept-authenticated-connection'"
  ([] (failing-gracefully
        (let [slime-secret-file (File. (str (user-home-path) File/separator ".slime-secret"))]
          (when (and (.isFile slime-secret-file) (.canRead slime-secret-file))
            (with-open [secret (BufferedReader. (FileReader. slime-secret-file))]
              (.readLine secret)))))))

(defn- accept-authenticated-connection ;; rename to authenticate-socket, takes in a connection
  "Accepts and returns new connection if it is from an authenticated
   machine. Otherwise, return nil.

   Authentication depends on the contents of a slime-secret file on
   both the server (swank) and the client (emacs slime). If no
   slime-secret file is provided on the server side, all connections
   are accepted.

   See also: `slime-secret'"
  ([#^Socket socket opts]
     (returning [conn (make-connection socket (get opts :encoding default-encoding))]
       (if-let [secret (slime-secret)]
         (when-not (= (read-from-connection conn) secret)
           (close-socket! socket))
         conn))))

(defn- make-output-redirection
  ([conn]
     (call-on-flush-stream
      #(with-connection conn
         (send-to-emacs `(:write-string ~%)))))
  {:tag java.io.StringWriter})

(defn- socket-serve [connection-serve socket opts]
  (with-connection (accept-authenticated-connection socket opts)
    (let [out-redir (java.io.PrintWriter. (make-output-redirection
                                           *current-connection*))]
      (binding [*out* out-redir
                *err* out-redir]
        (dosync (ref-set (*current-connection* :writer-redir) *out*))
        (dosync (alter connections conj *current-connection*))
        (connection-serve *current-connection*)))))

;; Setup frontent
(defn start-swank-socket-server!
  "Starts and returns the socket server as a swank host. Takes an
   optional set of options as a map:

    :announce - an fn that will be called and provided with the
    listening port of the newly established server. Default: none."
  ([server connection-serve] (start-swank-socket-server! connection-serve {}))
  ([server connection-serve options]
     (start-server-socket! server connection-serve)
     (when-let [announce (options :announce)]
       (announce (.getLocalPort server)))
     server))

(defn setup-server
  "The port it started on will be called as an argument to (announce-fn
   port). A connection will then be created and (connection-serve conn)
   will then be called."
  [port announce-fn connection-serve opts]
  (start-swank-socket-server!
   (make-server-socket {:port    port
                        :host    (opts :host "localhost")
                        :backlog (opts :backlog 0)})
   #(socket-serve connection-serve % opts)
   {:announce announce-fn}))

;; Announcement functions
(defn simple-announce [port]
  (println "Connection opened on local port " port))

(defn announce-port-to-file
  "Writes the given port number into a file."
  ([#^String file port]
     (with-open [out (new java.io.FileWriter file)]
       (doto out
         (.write (str port "\n"))
         (.flush)))))