forked from Neronus/clj-server
-
Notifications
You must be signed in to change notification settings - Fork 1
/
README
86 lines (75 loc) · 4.25 KB
/
README
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
Time-stamp: <2009-08-09 17:38:52 christian>
Christian von Essen <christian@mvonessen.de>
This project aims to provide a client-server architecture for clojure.
Clojure's startup time is pretty bad. Too bad, in fact, to use it as a platform
for scripting. So this project aims to provide a client-server architecture
compatible to normal usage of clojure. This means, that if you start the client
without parameters, the normal clojure REPL is started, and if you pass a file
name, that script is executed, and so forth. In fact, I copied clojure.main
and modified it to reflect my needs.
To achieve this, a server is listening for incoming connections on a configurable
port. If a client connects, the connection is used to transfer data that normally
goes through *in* *out* and *err*.
This server is intended for local use only. This means, that the server should
only listen to 127.0.0.1 or some other loopback address, as the data stream
is not encrypted. Furthermore, the authentication scheme only works locally.
REQUIREMENTS:
To check the file permissions, a POSIX library is required. The one
used can be found at
http://www.bmsi.com/java/posix/package.html.
Alternatively, there is a slightly modified version at
http://github.com/Neronus/clj-server/tree/master
PROTOCOL:
* Authentication:
As the server executes arbitrary code, some kind of authentication has to be
used. This authentication has to work without user interaction, has to be fast
and easy to setup, and should require no third party libraries, if possible.
This project uses a file based authentication method: A file, which
can only be read by the user who started the server is required. To
this end, when the communication between client and server begins, the
server send a filename to the client; the client then sends the
content of the file to the server, which compares the data sent to the
data which it reads from the file. If the recieved data and the
file's contents are equal, the connection is accepted, and the command
line parameter passing is started.
* Command line parameter passing:
First, the current working directory is transmitted, then the number of
command line arguments, then the command line arguments themself, if
there are any.
* Communication:
Communication is done via sockets, obviously. The client
just send its data recieved via stdin to the server. The server
OTOH uses a simple protocol to address filedescriptors when printing:
Everything that is sent from the server to the client
is wrapped into a 'chunk' (idea taken from Nailgun).
This chunk has a header consisting of 2 integers (4 byte each):
First, the number of the filedescriptor to be addressed is sent,
then the size of the chunk. After that, the contents of the
chunk are sent. The client recieves the chunk, and forwards
the data to the given FD. At the time of writing, only
two FDs are supported on the server side: stdout and stederr.
USAGE:
See the file 'clj-server' in the src/ directory for an example. On
the server side, you have to start a clojure instance, create a server
socket via (create-server <port> <backlog> <path>) (path is the path
of the file used for authorization. should be only readable by the
user starting the server), and then go into the accepting loop via
(server-loop <server-socket> <minThreads> <maxThreads>), where
minThreads is the number of threads you want to have for handling
connections, and maxThreads is the maximum number for the same job.
On the client side, you have to use the clj-client binary.
To configure this binary, there is one environment variable:
CLJ_CLIENT_PORT the port to connect to. Other than that, the client should work
just like using clojure.main in all cases.
Each session gets a unique namespace, exclsuively created for it.
After the session terminates, the namespace is deleted.
NOTES:
* By default, a temporary (that is, only existing for the session)
namespace is created. You can change this behaviour by setting
or binding clojure-server.main/*gensym-ns*
* Using vars (other than that of the temporary namespace) is not a
good idea, as they are persistend between sessions,
and possibly are set by another script.
* Each starting namespace gets a symbol *current-working-directory*,
which contains the absolute path of the current working directory
of the client.