A Common Lisp library implementing the MessagePack-RPC specification, which uses MessagePack serialization format to achieve efficient remote procedure calls (RPCs).
Library uses cl-messagepack and cl-async under the hood. Currently only the client side functionality is fully supported, but some server-side features (such as registering callbacks for sessions) are also implemented. Library supports connecting to the server via TCP sockets, named pipes or via user-specified streams (e.g. standard input and output streams).
Package is available through Quicklisp, so simply evaluating
* (ql:quickload :cl-messagepack-rpc)
from your REPL should properly load all the dependencies and cl-messagepack-rpc itself.
In order to get cl-async working, you will however also need libuv1-dev
package, which you can install using your distribution's package manager, or by manually compiling libuv.
This library tries to follow the official specification as closely as possible. For a quick taste:
(ql:quickload :cl-messagepack-rpc)
(defparameter *client* (make-instance 'mrpc:client :file "/path/to/named/pipe"))
(mrpc:call *client* "echo" "Hello server!")
;=> "Hello server!"
(defparameter *future* (mrpc:call-async *client* "execute_some_long_task"))
;=> *future*
(mrpc:request *client* "add" 3 2) ; mrpc:request is an alias for mrpc:call
;=> 5
(mrpc:join *future*) ; calling join on future also returns its result (or throws an error)
;=> "Done with the execution of some long task!"
(mrpc:notify *client* "client_finished")
;=> T
Main class used to connect to an already running server. You can specify which means of transport the server uses via make-instance
call:
(defparameter *client* (make-instance 'client :host "127.0.0.1" :port 1985)) ; to connect via TCP
(defparameter *client2* (make-instance 'client :file "/path/to/named/pipe")) ; to connect via named pipe
(defparameter *client3* (make-instance 'client)) ; to connect via standard input/output
(defparameter *client4* (make-instance 'client :input-stream *standard-input*
:output-stream *output-stream*))
; *client4* is same as *client3*, but note that you can specify any (binary) input and output streams
If remote server uses extended types, you can specify that by passing a specification list via :extended-types
argument to #'make-instance. For example, to translate the use case from cl-messagepack's readme, you would use:
(defparameter *client* (make-instance 'client :host "127.0.0.1" :port 1985)
:extended-types '(:numeric 0 Buffer Window Tabpage))
Objects of extended type can be tested for equality using #'eq
.
Register a CALLBACK with name METHOD for some SESSION. Callback should be something callable:
(register-callback *client* "add" #'(lambda (a b) (+ a b)))
Remove a previously registered callback with name METHOD from SESSION.
(remove-callback *client* "add")
Use SESSION to call METHOD with PARAMS and immediately return control to the caller, returning a future object.
(call-async *client* "server_side_add" 1 2 3)
;=> #<FUTURE {100962B8F3}>
Invoke CALL-ASYNC on the passed arguments, and call JOIN on the returned future.
(call *client* "server_side_add" 1 2 3)
;=> 6
Alias for CALL.
Use SESSION to call METHOD with PARAMS, immediately returning control to the caller. This call completely ignores server responses.
(notify *client* "do_something")
;=> T
Class used to hold responses from the server. You should not need to create future objects by hand.
Block execution until FUTURE has a result from the server. Then either return a result, or throw an error, depending on how the server responded.
(let ((future (call-async *client* "add" 3 2)))
; do something...
(join future))
;=> 5
(let ((future (call-async *client* "add" 3 "some string")))
; do something...
(join future))
;=> ERROR: unexpected types of arguments.
Because server-side support is not yet implemented, tests use a python based server. So in order to test the library, first start the python server:
$ python t/server.py
and then evaluate
* (asdf:test-system :cl-messagepack-rpc)
in the REPL to run the actual tests.
The library is developed and tested with sbcl
under Debian GNU/Linux, but should work everywhere cl-async does.
Copyright (c) 2016 Andrej Dolenc
Licensed under the MIT License.