Skip to content

Commit 1aba017

Browse files
authored
Add support for Unix domain socket connection (#3088)
1 parent a88c97a commit 1aba017

File tree

5 files changed

+77
-16
lines changed

5 files changed

+77
-16
lines changed

cider-connection.el

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,8 @@ PARAMS is a plist containing :host, :port, :server and other parameters for
8585
(plist-get params :port)
8686
(plist-get params :server)
8787
(lambda (_)
88-
(cider-repl-create params)))))
88+
(cider-repl-create params))
89+
(plist-get params :socket-file))))
8990

9091
(defun cider-sessions ()
9192
"Return a list of all active CIDER sessions."

cider.el

Lines changed: 23 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1398,7 +1398,7 @@ non-nil, don't start if ClojureScript requirements are not met."
13981398
(user-error "The %s executable isn't on your `exec-path'" command))))
13991399

14001400
(defun cider--update-host-port (params)
1401-
"Update :host and :port in PARAMS."
1401+
"Update :host and :port; or :socket-file in PARAMS."
14021402
(with-current-buffer (or (plist-get params :--context-buffer)
14031403
(current-buffer))
14041404
(let* ((params (cider--update-do-prompt params))
@@ -1409,9 +1409,11 @@ non-nil, don't start if ClojureScript requirements are not met."
14091409
(if (and host port)
14101410
(cons host port)
14111411
(cider-select-endpoint)))))
1412-
(thread-first params
1413-
(plist-put :host (car endpoint))
1414-
(plist-put :port (cdr endpoint))))))
1412+
(if (equal "local-unix-domain-socket" (car endpoint))
1413+
(plist-put params :socket-file (cdr endpoint))
1414+
(thread-first params
1415+
(plist-put :host (car endpoint))
1416+
(plist-put :port (cdr endpoint)))))))
14151417

14161418
(defun cider--update-cljs-init-function (params)
14171419
"Update PARAMS :repl-init-function for cljs connections."
@@ -1491,11 +1493,14 @@ canceled the action, signal quit."
14911493
cider-known-endpoints
14921494
ssh-hosts
14931495
;; always add localhost
1494-
'(("localhost")))))
1496+
'(("localhost")
1497+
("local-unix-domain-socket")))))
14951498
(sel-host (cider--completing-read-host hosts))
14961499
(host (car sel-host))
14971500
(port (or (cadr sel-host)
1498-
(cider--completing-read-port host (cider--infer-ports host ssh-hosts)))))
1501+
(if (equal host "local-unix-domain-socket")
1502+
(cider--completing-read-socket-file)
1503+
(cider--completing-read-port host (cider--infer-ports host ssh-hosts))))))
14991504
(cons host port)))
15001505

15011506
(defun cider--ssh-hosts ()
@@ -1563,6 +1568,18 @@ of remote SSH hosts."
15631568
(port (if (listp port) (cadr port) port)))
15641569
(if (stringp port) (string-to-number port) port)))
15651570

1571+
(defun cider--completing-read-socket-file ()
1572+
"Interactively select unix domain socket file name."
1573+
(read-file-name "Socket File: " nil nil t nil
1574+
(lambda (filename)
1575+
"Predicate: auto-complete only socket-files and directories"
1576+
(let ((filetype (string-to-char
1577+
(file-attribute-modes
1578+
(file-attributes
1579+
filename)))))
1580+
(or (eq ?s filetype)
1581+
(eq ?d filetype))))))
1582+
15661583
(defun cider-locate-running-nrepl-ports (&optional dir)
15671584
"Locate ports of running nREPL servers.
15681585
When DIR is non-nil also look for nREPL port files in DIR. Return a list

doc/modules/ROOT/pages/basics/up_and_running.adoc

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,26 @@ cause problems with complex or nonstandard ssh configs.
269269
You can safely run `cider-jack-in-*` while working with remote files over TRAMP. CIDER
270270
will handle this use-case transparently for you.
271271

272+
== Connecting via unix domain file socket
273+
274+
When locally running nREPL servers, there is the option to listen on a
275+
socket file instead of opening a network port. This can have
276+
advantages in some use cases, e.g. when working with virtual networks
277+
(containers) where sharing a file socket can be vastly simpler than
278+
managing bridge networks and firewall setups.
279+
280+
After having started an nREPL server on a file socket, e.g. with the
281+
`clj` command (see https://nrepl.org/nrepl/usage/server.html for other
282+
examples),
283+
284+
[source,sh]
285+
----
286+
$ clj -R:nREPL -m nrepl.cmdline --socket nrepl.sock
287+
----
288+
289+
you can then connect CIDER by using the `local-unix-domain-socket`
290+
special hostname with `cider-connect`: kbd:[M-x] `cider-connect` kbd:[RET] `local-unix-domain-socket` kbd:[RET] `nrepl.sock` kbd:[RET]
291+
272292
== What's Next?
273293

274294
So, what to do next now that CIDER's ready for action? Here are a few ideas:

nrepl-client.el

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -491,6 +491,26 @@ and kill the process buffer."
491491

492492
;;; Network
493493

494+
(defun nrepl--unix-connect (socket-file &optional no-error)
495+
"If SOCKET-FILE is given, try to `make-network-process'.
496+
If NO-ERROR is non-nil, show messages instead of throwing an error."
497+
(if (not socket-file)
498+
(unless no-error
499+
(error "[nREPL] Socket file not provided"))
500+
(message "[nREPL] Establishing unix socket connection to %s ..." socket-file)
501+
(condition-case nil
502+
(prog1 (list :proc (make-network-process :name "nrepl-connection" :buffer nil
503+
:family 'local :service socket-file)
504+
:host "local-unix-domain-socket"
505+
:port socket-file
506+
:socket-file socket-file)
507+
(message "[nREPL] Unix socket connection to %s established" socket-file))
508+
(error (let ((msg (format "[nREPL] Unix socket connection to %s failed" socket-file)))
509+
(if no-error
510+
(message msg)
511+
(error msg))
512+
nil)))))
513+
494514
(defun nrepl-connect (host port)
495515
"Connect to the nREPL server identified by HOST and PORT.
496516
For local hosts use a direct connection. For remote hosts, if
@@ -629,14 +649,16 @@ Do not kill the server if there is a REPL connected to that server."
629649
(buffer-list)))
630650
(nrepl-kill-server-buffer server-buf)))))
631651

632-
(defun nrepl-start-client-process (&optional host port server-proc buffer-builder)
633-
"Create new client process identified by HOST and PORT.
634-
In remote buffers, HOST and PORT are taken from the current tramp
635-
connection. SERVER-PROC must be a running nREPL server process within
636-
Emacs. BUFFER-BUILDER is a function of one argument (endpoint returned by
637-
`nrepl-connect') which returns a client buffer. Return the newly created
638-
client process."
639-
(let* ((endpoint (nrepl-connect host port))
652+
(defun nrepl-start-client-process (&optional host port server-proc buffer-builder socket-file)
653+
"Create new client process identified by either HOST and PORT or SOCKET-FILE.
654+
If SOCKET-FILE is non-nil, it takes precedence. In remote buffers, HOST
655+
and PORT are taken from the current tramp connection. SERVER-PROC must be
656+
a running nREPL server process within Emacs. BUFFER-BUILDER is a function
657+
of one argument (endpoint returned by `nrepl-connect') which returns a
658+
client buffer. Return the newly created client process."
659+
(let* ((endpoint (if socket-file
660+
(nrepl--unix-connect (expand-file-name socket-file))
661+
(nrepl-connect host port)))
640662
(client-proc (plist-get endpoint :proc))
641663
(builder (or buffer-builder (error "`buffer-builder' must be provided")))
642664
(client-buf (funcall builder endpoint)))

test/nrepl-client-tests.el

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,8 @@
168168
(plist-get server-endpoint :port)
169169
server-process
170170
(lambda (client-endpoint)
171-
client-buffer))))
171+
client-buffer)
172+
(plist-get params :socket-file))))
172173

173174
;; client connection is open
174175
(expect (process-status process-client)

0 commit comments

Comments
 (0)