@@ -4,10 +4,14 @@ using REPL
4
4
using Logging
5
5
6
6
mutable struct ServerSideSession
7
+ socket
7
8
display_properties:: Dict
8
9
in_module:: Module
9
10
end
10
11
12
+ Base. isopen (session:: ServerSideSession ) = isopen (session. socket)
13
+ Base. close (session:: ServerSideSession ) = close (session. socket)
14
+
11
15
function send_header (io, ser_version= Serialization. ser_version)
12
16
write (io, PROTOCOL_MAGIC, PROTOCOL_VERSION)
13
17
write (io, UInt32 (ser_version))
@@ -93,9 +97,7 @@ function eval_message(session, messageid, messagebody)
93
97
end
94
98
end
95
99
96
- function evaluate_requests (request_chan, response_chan)
97
- session = ServerSideSession (Dict (), Main)
98
-
100
+ function evaluate_requests (session, request_chan, response_chan)
99
101
while true
100
102
try
101
103
request = take! (request_chan)
@@ -174,15 +176,16 @@ function serialize_responses(socket, response_chan)
174
176
end
175
177
end
176
178
177
- # Serve a remote REPL session to a single client over `socket`.
178
- function serve_repl_session (socket)
179
+ # Serve a remote REPL session to a single client
180
+ function serve_repl_session (session)
181
+ socket = session. socket
179
182
send_header (socket)
180
183
@sync begin
181
184
request_chan = Channel (1 )
182
185
response_chan = Channel (1 )
183
186
184
187
repl_backend = @async try
185
- evaluate_requests (request_chan, response_chan)
188
+ evaluate_requests (session, request_chan, response_chan)
186
189
catch exc
187
190
@error " RemoteREPL backend crashed" exception= exc,catch_backtrace ()
188
191
finally
@@ -210,14 +213,18 @@ function serve_repl_session(socket)
210
213
end
211
214
212
215
"""
213
- serve_repl([address=Sockets.localhost,] port=$DEFAULT_PORT )
216
+ serve_repl([address=Sockets.localhost,] port=$DEFAULT_PORT ; [on_client_connect=nothing] )
214
217
serve_repl(server)
215
218
216
219
Start a REPL server listening on interface `address` and `port`. In normal
217
220
operation `serve_repl()` serves REPL clients indefinitely (ie., it does not
218
221
return), so you will generally want to launch it using `@async serve_repl()` to
219
222
do other useful work at the same time.
220
223
224
+ The hook `on_client_connect` may be supplied to modify the `ServerSideSession`
225
+ for a client after each client connects. This can be used to define the default
226
+ module in which the client evaluates commands.
227
+
221
228
If you want to be able to stop the server you can pass an already-listening
222
229
`server` object (the result of `Sockets.listen()`). The server can then be
223
230
cancelled from another task using `close(server)` as necessary to control the
@@ -230,34 +237,38 @@ be used on open networks or multi-user machines where other users aren't
230
237
trusted. For open networks, use the default `address=Sockets.localhost` and the
231
238
automatic ssh tunnel support provided by the client-side `connect_repl()`.
232
239
"""
233
- function serve_repl (address= Sockets. localhost, port:: Integer = DEFAULT_PORT)
240
+ function serve_repl (address= Sockets. localhost, port:: Integer = DEFAULT_PORT; kws ... )
234
241
server = listen (address, port)
235
242
try
236
- serve_repl (server)
243
+ serve_repl (server; kws ... )
237
244
finally
238
245
close (server)
239
246
end
240
247
end
241
- serve_repl (port:: Integer ) = serve_repl (Sockets. localhost, port)
248
+ serve_repl (port:: Integer ; kws ... ) = serve_repl (Sockets. localhost, port; kws ... )
242
249
243
- function serve_repl (server:: Base.IOServer )
244
- open_sockets = Set ()
250
+ function serve_repl (server:: Base.IOServer ; on_client_connect = nothing )
251
+ open_sessions = Set {ServerSideSession} ()
245
252
@sync try
246
253
while isopen (server)
247
254
socket = accept (server)
248
- push! (open_sockets, socket)
249
- peer= getpeername (socket)
255
+ session = ServerSideSession (socket, Dict (), Main)
256
+ push! (open_sessions, session)
257
+ peer = getpeername (socket)
250
258
@async try
251
- serve_repl_session (socket)
259
+ if ! isnothing (on_client_connect)
260
+ on_client_connect (session)
261
+ end
262
+ serve_repl_session (session)
252
263
catch exc
253
- if ! (exc isa EOFError && ! isopen (socket ))
264
+ if ! (exc isa EOFError && ! isopen (session ))
254
265
@warn " Something went wrong evaluating client command" #=
255
266
=# exception= exc,catch_backtrace ()
256
267
end
257
268
finally
258
269
@info " REPL client exited" peer
259
- close (socket )
260
- pop! (open_sockets, socket )
270
+ close (session )
271
+ pop! (open_sessions, session )
261
272
end
262
273
@info " REPL client opened a connection" peer
263
274
end
@@ -269,8 +280,8 @@ function serve_repl(server::Base.IOServer)
269
280
@error " Unexpected server failure" isopen (server) exception= exc,catch_backtrace ()
270
281
rethrow ()
271
282
finally
272
- for socket in open_sockets
273
- close (socket )
283
+ for session in open_sessions
284
+ close (session )
274
285
end
275
286
end
276
287
end
0 commit comments