0

I'm trying to run clojure.core.server/start-server but instead of using the repl I want a custom function as accept option. I'm following this post where a repl server is executed as

clojure -X clojure.core.server/start-server :name '"server"' :port 5555 :accept clojure.core.server/repl :server-daemon false

What requirements do I need to pass a function to accept opt? What if I only want to print the request from any connection? Using something like clojure.core/println didn't work

clojure -X clojure.core.server/start-server :name '"server"' :port 5555 :accept clojure.core/println :server-daemon false

btw, I can't even run start-server on the repl itself, I've got the error everytime a made a request. Is it possible to run it from the repl?

(clojure.core.server/start-server {:name "server" :port 9000 :accept clojure.core.server/repl :server-daemon false})
#object[java.net.ServerSocket 0x25b865b5 "ServerSocket[addr=localhost/127.0.0.1,localport=9000]"]
user=> Exception in thread "Clojure Connection server 1" java.lang.ClassCastException: class clojure.core.server$repl cannot be cast to class clojure.lang.Named (clojure.core.server$repl and clojure.lang.Named are in unnamed module of loader 'app')
        at clojure.core$namespace.invokeStatic(core.clj:1612)
        at clojure.core.server$accept_connection.invokeStatic(server.clj:73)
        at clojure.core.server$start_server$fn__8998$fn__8999$fn__9001.invoke(server.clj:117)
        at clojure.lang.AFn.run(AFn.java:22)
        at java.base/java.lang.Thread.run(Thread.java:833)

Rafael
  • 555
  • 6
  • 19

1 Answers1

1

The start-server function states, that :accept must be a namespaced symbol (so this is the reason, why you see the error in your REPL (you are passing a variable; use 'clojure.core/println instead -- note the leading ').

As for passing your own function, it helps to take a look at the function clojure.core.server/accept-connection, what is actually going on. So basically your :accept function gets called with *in*, *out*, *err* redirected (and also a binding of *session* with some book-keeping) and the :args. So you want to read/write on stdin/stdout in your function and parameterize with the given arguments.

E.g. create a file src/so.clj with the following content:

(ns so)

(defn accept
  [prefix]
  (loop []
    (println prefix (read-line))
    (recur)))

Then start the server from the same root:

clojure -X clojure.core.server/start-server :name '"server"' :port 5555 \
  :accept so/accept \
  :args '[">>>"]' \
  :server-daemon false

Note the :accept symbol (it's coming from the shell, so Clojure reads that as a symbol) points to the namespace and the function within. Make sure, things match up with the class-path (src is a default source path for the Clojure CLI).

Also note the passing of the :args represented as an EDN array. Inside accept-connection the call to your function is then (apply accept args), so you can assume regular arguments in your accept-fn.

Then test your server with e.g. telnet:

% telnet localhost 5555
Trying 127.0.0.1...
Connected to localhost.
Escape character is '^]'.
hello
>>> hello
world
>>> world
cfrick
  • 35,203
  • 6
  • 56
  • 68