Description

remote read-eval-print loop client and server

Author

elf

Version

Usage

(require-extension remote-repl)

Download

remote-repl.egg

Documentation

This extension provides a safe, portable, very flexible remote REPL facility. The server component is fully threaded and may be plugged into any existing codebase without modification. The client component is not threaded.

For maximum flexibility, all procedures involved in the operation of these extensions may be specified. The default is to use the tcp extension with no authentication and no sandboxing, but the design should make it easy to extend (e.g. through SSL sockets, ACL authentication, shared or unshared environments, etc) while maintaining safety. All conditions are caught and handled properly, and no knowledge of threading, exception handling, or chicken internals is required. Just plug it in and go!

Server Procedures

For the server component, use: (require-extension remote-repl-server)

procedure: (rrepl-server-start PORTNUM [KEYARGS...])

Creates a new remote-repl server and returns a rrepl-server object. The server automatically starts a listener thread, waiting for connections on PORTNUM. KEYARGS are optional keyword arguments for extending/tuning the server's behaviour, as given below:

Keyword Default Description
listen tcp-listen

Procedure to create a listener for incoming connections. Must take three arguments: an integer portnum on which to listen, an integer count of maximum pending connections, and a string (or #f) corresponding to a hostname/IP. The final string argument, if given, is the only host from which connections are allowed.

accept tcp-accept

Procedure to accept an incoming connection from a listener port. Takes in a listener argument and returns an input port and an output port for the new connection.

clisten tcp-close

Procedure to close and release a listener port given as an argument.

auth identity

After accepting a connection with accept, the auth procedure is evaluated on the server object and the new session object created from the ports. If the session authenticates, it is returned; if it does not pass, return #f. The REPL thread for a session is not started until after authentication.

host #f

If given, only accept connections originating from this hostname/IP. See listen for more details.

backlog 4

Maximum number of pending connections. See listen for more details.

timeout-read 50000

Timeout in milliseconds before aborting a read operation. NOTE: DO NOT SET THIS TO #f! Timeouts are handled gracefully and will not generate errors, and are required for proper operation.

timeout-write 50000

Timeout in milliseconds before aborting a write operation. NOTE: DO NOT SET THIS TO #f! Timeouts are handled gracefully and will not generate errors, and are required for proper operation.

timeout-accept 50000

Timeout in milliseconds before aborting an accept operation. NOTE: DO NOT SET THIS TO #f! Timeouts are handled gracefully and will not generate errors, and are required for proper operation.

reader read

Procedure to read from a session's input port.

evaler eval

Procedure for evaluating input in a session. Takes TWO arguments, the expression to evaluate and the session object. This allows the rrepl-session:extra field to be used, if desired.

printer write + newline

Procedure for displaying evaluated output to a session's output port. Takes two arguments, the object to be written and the output port. NOTE: A NEWLINE MUST BE ADDED BY THIS PROCEDURE FOLLOWING OUTPUT.

close-in close-input-port

Procedure for closing a session's input port.

close-out close-output-port

Procedure for closing a session's output port.

procedure: (rrepl-server-close SERVER-OBJ)

Closes the remote-repl server specified by SERVER-OBJ. This includes closing the listener port and all active sessions.

RREPL-SERVER Procedures
procedure: (rrepl-server? OBJ)

Returns #t if OBJ is a rrepl-server object.

procedure: (rrepl-server:close-listen SERVER-OBJ)

Returns the procedure for closing the listener port. This is a wrapper around the procedure specified by the keyword clisten for condition safety and metadata cleanup.

procedure: (rrepl-server:read SERVER-OBJ)

Returns the procedure for reading from a session's input port, as specified by the reader keyword.

procedure: (rrepl-server:eval SERVER-OBJ)

Returns the procedure for evaluating input in a session, as specified by the evaler keyword.

procedure: (rrepl-server:print SERVER-OBJ)

Returns the procedure for printing to a session's output port, as specified by the printer keyword.

procedure: (rrepl-server:close SERVER-OBJ)

Returns a procedure that takes a rrepl-server object and a rrepl-session object, and shuts down the session, after closing the ports. Uses the procedures specified by the close-in and close-out keywords.

procedure: (rrepl-server:listener SERVER-OBJ)

Returns the listener port associated with SERVER-OBJ, or #f if not accepting connections.

procedure: (rrepl-server:listen-thr SERVER-OBJ)

Returns SERVER-OBJ's listener thread.

procedure: (rrepl-server:remotes SERVER-OBJ)

Returns a list of currently active rrepl-session objects connected to SERVER-OBJ.

procedure: (rrepl-server:accepting SERVER-OBJ)

Returns #t if SERVER-OBJ is accepting new connections, or #f otherwise.

procedure: (rrepl-server:accepting! SERVER-OBJ BOOL)

Sets the accepting-new-connections state of SERVER-OBJ to BOOL. This is intended for use in auth procedures to force the listener port to close after a certain number of connections. Setting this to #f will irrevocably kill the listener port.

procedure: (rrepl-server:active SERVER-OBJ)

Returns #t if SERVER-OBJ is currently active, or #f if it is closed. Note: A server running with a closed listener port is explicitly allowed.

procedure: (rrepl-server:active! SERVER-OBJ BOOL)

Sets the running state of SERVER-OBJ to BOOL. This should generally never be used, as setting to #f will shut down the server without proper cleanup.

RREPL-SESSION Procedures
procedure: (rrepl-session? OBJ)

Returns #t if OBJ is a rrepl-session object.

procedure: (rrepl-session:inport SESSION-OBJ)

Returns the input socket port associated with SESSION-OBJ.

procedure: (rrepl-session:outport SESSION-OBJ)

Returns the output socket port associated with SESSION-OBJ.

procedure: (rrepl-session:thread SESSION-OBJ)

Returns SESSION-OBJ's REPL thread.

procedure: (rrepl-session:extra SESSION-OBJ)

Returns SESSION-OBJ's "extra" object (initially #f). This is an arbitrary user-settable object for augmenting any other functionality.

procedure: (rrepl-session:extra! SESSION-OBJ OBJ)

Sets the "extra" object for SESSION-OBJ to OBJ. OBJ may be any object.

procedure: (rrepl-session:active SESSION-OBJ)

Returns #t if session is open and active, or #f otherwise.

procedure: (rrepl-session:active! SESSION-OBJECT BOOL)

Sets the running state of SESSION-OBJ to BOOL. This should generally never be used, as setting to #f will end the session without proper cleanup.

Client Procedures

For the client component, use: (require-extension remote-repl-client)

procedure: (rrepl-client-connect HOSTNAME PORTNUM [KEYARGS...])

Creates a new remote-repl client and returns a rrepl-client object. The server automatically connects and authenticates to HOSTNAME on port PORTNUM. KEYARGS are optional keyword arguments for extending/tuning the client's behaviour, as given below:

Keyword Default Description
connect tcp-connect

Procedure to create a socket connection to a remote host. Must take the hostname and portnum as its arguments, and returns an input port and an output port.

auth identity

After connecting with connect, the auth procedure is evaluated on a new rrepl-client object to perform any authentication steps before handing off to the user. Returns the rrepl-client object on success, or #f on failure.

reader read-line

Procedure to read the output (one line) from the remote REPL.

printer write + newline

Procedure for sending expressions to the output port for evaluation by the remote REPL. Takes two arguments, the expression to be run and the output port. NOTE: A NEWLINE MUST BE ADDED BY THIS PROCEDURE FOLLOWING OUTPUT.

close-in close-input-port

Procedure for closing a client's input port.

close-out close-output-port

Procedure for closing a client's output port.

procedure: (rrepl-client-send CLIENT-OBJ EXPRESSION)

Sends EXPRESSION to the remote REPL for evaluation, and returns the result as a string.

procedure: (rrepl-client-close CLIENT-OBJ)

Closes the remote REPL client specified by CLIENT-OBJ. This includes closing the ports and notifying the server that the session is over.

RREPL-CLIENT Procedures
procedure: (rrepl-client? OBJ)

Returns #t if OBJ is a rrepl-client object.

procedure: (rrepl-client:read CLIENT-OBJ)

Returns the procedure for reading from a client's input port, as specified by the reader keyword.

procedure: (rrepl-client:print CLIENT-OBJ)

Returns the procedure for printing to a client's output port, as specified by the printer keyword.

procedure: (rrepl-client:close CLIENT-OBJ)

Returns a procedure that takes a rrepl-client object, and shuts down the client, after closing the ports. Uses the procedures specified by the close-in and close-out keywords.

procedure: (rrepl-client:inport CLIENT-OBJ)

Returns the input socket port associated with CLIENT-OBJ.

procedure: (rrepl-client:outport CLIENT-OBJ)

Returns the output socket port associated with CLIENT-OBJ.

procedure: (rrepl-client:active CLIENT-OBJ)

Returns #t if client is connected and active, or #f otherwise.

Bugs

None known

Examples

Simplest (default) usage, with tcp connections and no auth:

> (require-extension remote-repl-server)
; loading chicken/3/remote-repl-server.so ...
> (define srv (rrepl-server-start 5555))
> (require-extension remote-repl-client)
; loading chicken/3/remote-repl-client.so ...
> (define cli (rrepl-client-connect "localhost" 5555))
> (rrepl-client-send cli '(define foo 3))
"#<unspecified>"
> (rrepl-client-send cli 'foo)
"3"
> (rrepl-client-close cli)
shutting down session...
> (rrepl-server-close srv)
"#t"

Using SSL with no authentication and restricting to local connections:

> (require-extension openssl)
; loading chicken/3/openssl.so ...
> (require-extension remote-repl-server)
; loading chicken/3/remote-repl-server.so ...
> (define srv (rrepl-server-start 5555 
                                  host: "localhost"
                                  listen: ssl-listen
                                  accept: ssl-accept
                                  clisten: ssl-close))
> (require-extension remote-repl-client)
; loading chicken/3/remote-repl-client.so ...
> (define cli (rrepl-client-connect "localhost" 5555
                                    connect: ssl-connect))
> (rrepl-client-send cli '(define foo 3))
"#<unspecified>"
> (rrepl-client-send cli 'foo)
"3"
> (rrepl-client-close cli)
shutting down session...
> (rrepl-server-close srv)
"#t"

Sandboxed evaluation for each session, tcp connections, no authentication:

> (require-extension sandbox)
; loading chicken/3/sandbox.so ...
> (require-extension remote-repl-server)
; loading chicken/3/remote-repl-server.so ...
> (define srv (rrepl-server-start 5555
                                  auth: (lambda (serv sess)
                                            (rrepl-session:extra! sess
                                                (make-safe-environment
                                                    parent: (current-environment)
                                                    mutable: #t
                                                    extendable: #t))
                                            sess)
                                  evaler: (lambda (expr sess)
                                              (eval expr
                                                    (rrepl-session:extra sess)))))
> (require-extension remote-repl-client)
; loading chicken/3/remote-repl-client.so ...
> (define cli (rrepl-client-connect "localhost" 5555))
> (rrepl-client-send cli '(define foo 3))
"#<unspecified>"
> (rrepl-client-send cli 'foo)
"3"
> (define cli2 (rrepl-client-connect "localhost" 5555))
> (rrepl-client-send cli2 'foo)
"\"condition: (exn) - unbound variable   args: (foo)\""
> (rrepl-client-close cli)
shutting down session...
> (rrepl-client-close cli2)
shutting down session...
> (rrepl-server-close srv)
"#t"

Licence

Copyright (C) 2008, Elf.  All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the Software),
to deal in the Software without restriction, including without limitation 
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the 
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED AS-IS, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR 
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.