An implementation of the JSON-RPC 2.0 protocol.
Note: There is only a client implementation for now.
With this egg you can setup a connection to a JSON-RPC server and send a JSON-RPC valid string without the overhead of having to build the correctly formatted JSON string yourself. Once a connection is established, the egg will listen on the specified input port for public messages from the server and will present server responses or errors to you in CHICKEN data.
The code is hosted on an external repository and can be found here.
Documentation and examples
Client[procedure] (json-rpc-server [input-port] [output-port] #!key version #!key full-data #!key public-receiver #!key test-mode)
In short, this will return a procedure that accepts a method (as a STRING) and optional params (as KEYWORD ARGUMENTS).
Once called, this method will send a valid JSON-RPC formatted string to the server. If applicable, a server response or server error will be returned to the user.
And in more detail
You can define the connection to your JSON-RPC server with the above procedure.
When defining your connection, you have to give an OUTPUT PORT and INPUT PORT. This implementation is made for version 2.0, so the version string is already set for you as a keyword argument. If a future version is released, I'll try to make it compatible.
The following (keyword) arguments are set with a sensible default, but could be overridden when defining your connection:
- version: The JSON-RPC version. Defaults to a STRING "2.0".
- full-data: The scope of the data you wish to have returned from the server. Defaults to #f which means it will return only the "result" part as specified in the spec.
- public-receiver: The optional procedure a user can set that will be invoked on receiving a public message from the server. Defaults to #f.
- test-mode: A flag to be set when running tests with this egg. Defaults to #f.
For demonstration purposes: Let's define a connection to a XBMC media center, which uses JSON-RPC for its remote calls:
(define xbmc (json-rpc-server input output))
Once setup, this will return a procedure that accepts a method and optional params. You can then call this procedure to actually send a valid JSON-RPC formatted string to the server.
Important to note is that the method can either be given as a string or a symbol:
- String: Send as a normal request (default)
- Symbol: Send as a notification request
A notification is a way to tell the server you do not need a response to your call, as laid out in the JSON-RPC 2 spec.
Let's ask the server for a listing of the current active players:
This will build the following CHICKEN data type:
((jsonrpc . 2.0) (method . Player.GetActivePlayers) (id . 1))
Then it will get formatted to valid JSON and send to the server.
The transformation from CHICKEN data types to JSON (and back) is done trough the wonderful Medea.
You can optionally include params to be send with your method. The JSON-RPC spec defines two types of params:
- Positional, which will be represented as literal symbols
- Named, which will be represented as keyword arguments
Let's see this in action on the same XBMC connection.
Take, for instance, sending a command to play or pause your XBMC box. Here you not only need to send the method, but also send which player you wish to pause or play. This would look something like the following:
(xbmc "Player.PlayPause" playerid: 0)
Which will be represented like so:
((jsonrpc . 2.0) (method . Player.PlayPause) (params (playerid . 0)) (id . 1))
And finally, you can nest your params.
We now want to know the title, album and artist of a certain playlist. For this we can use a vector inside a listing of keyword arguments:
(xbmc "Playlist.GetItems" properties: '#("title" "album" "artist") playlistid: 0)
And this will become:
((jsonrpc . 2.0) (method . Playlist.GetItems) (params (properties: . #(title album artist)) (playlistid: . 0)) (id . 1))
One more, just for fun.
After getting bored of listening to music, we get an urge to make a selection of available television shows. Here we don't use a vector, but we nest keyword arguments:
(xbmc "VideoLibrary.GetTVShows" filter: '((field: . "playcount")(operator: . "is")(value: . "0")))
((jsonrpc . 2.0) (method . VideoLibrary.GetTVShows) (params (filter: (field: . playcount) (operator: . is) (value: . 0))) (id . 1))
By default the JSON-RPC server will respond to all requests made, unless the request is set to be a notification. The server response will be parsed and returned to you immediately as CHICKEN data.
A JSON-RPC server is also allowed to send public messages at any given point in time. The JSON-RPC egg will capture these message for you as soon as they are broadcast, but it is up to the user of this egg to decide what to do with them. For this the user can set a procedure when defining a server connection using the public-receiver keyword argument.
There is no server implementation for now.
Real life example
Below is a short, start to finish example of how you can use this egg: how you can setup an input and output port, setup your connection and actually send something over that connection.
;Load in the TCP Unit and this egg (use tcp json-rpc-client) ;Create an input and output port to a server listening at 192.168.0.10 on port 9090 (define-values (input output) (tcp-connect "192.168.0.10" 9090)) ;Define the connection to the server (called xbmc here) (define xbmc (json-rpc-server input output)) ;Send an actual, non-notification command over the connection you just made (xbmc "Player.PlayPause" playerid: 0) ; > ((item (label . "Numina - Cold Shine") (type . "Relax music"))) ; Possible server response ;Or send the same command, but as a notification (Give the method as a symbol instead of a string) (xbmc 'Player.PlayPause playerid: 0)
- Implemented mailbox-threads for continuous port listening and message dispatching
- Refactored the notification setting trough a tiny hack, giving a sensible default
- Added JSON-RPC 2.0 spec compliant "notification" flag when sending client requests
- Removed 'filter' and added 'remove' instead, tests now up and running correctly
- Fixed wrong version setup number, removed 'append' and added 'filter' instead
- Code cleanup and made 'version' an implicit argument
- Fixed erroneous dependency.
- Initial Chicken 4 release.
Copyright (c) 2013, Tim van der Linden All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. Neither the name of the author nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.