Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/carlalbrecht/socket-json-rpc
An asynchronous JSON-RPC server and client for Clojure that uses sockets for communication
https://github.com/carlalbrecht/socket-json-rpc
async clojure clojure-library json-rpc json-rpc-client json-rpc-server json-rpc2 socket-io
Last synced: 3 months ago
JSON representation
An asynchronous JSON-RPC server and client for Clojure that uses sockets for communication
- Host: GitHub
- URL: https://github.com/carlalbrecht/socket-json-rpc
- Owner: carlalbrecht
- License: epl-1.0
- Archived: true
- Created: 2017-03-15T02:46:15.000Z (almost 8 years ago)
- Default Branch: master
- Last Pushed: 2017-03-23T05:39:21.000Z (almost 8 years ago)
- Last Synced: 2024-09-22T07:03:49.533Z (3 months ago)
- Topics: async, clojure, clojure-library, json-rpc, json-rpc-client, json-rpc-server, json-rpc2, socket-io
- Language: Clojure
- Homepage:
- Size: 49.8 KB
- Stars: 0
- Watchers: 1
- Forks: 0
- Open Issues: 1
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Awesome Lists containing this project
README
# socket-json-rpc
[![Build Status](https://travis-ci.org/invlpg/socket-json-rpc.svg?branch=master)](https://travis-ci.org/invlpg/socket-json-rpc)
[![codecov](https://codecov.io/gh/invlpg/socket-json-rpc/branch/master/graph/badge.svg)](https://codecov.io/gh/invlpg/socket-json-rpc)
[![Dependencies Status](https://jarkeeper.com/invlpg/socket-json-rpc/status.svg)](https://jarkeeper.com/invlpg/socket-json-rpc)[![Clojars Project](https://img.shields.io/clojars/v/invlpg/socket-json-rpc.svg)](https://clojars.org/invlpg/socket-json-rpc)
socket-json-rpc is a Clojure library that simplifies creating async JSON-RPC
servers and clients that communicate using TCP sockets.## Server Usage
First, require the server:
```clojure
(require '[socket-json-rpc.server :as server])
```Procedures are defined with `defprocedure`, and is used in the form
```clojure
(server/defprocedure [name] [[named-args]] [body])
```The named-args argument tells the server how to arrange named arguments into a
vector so that the body can use the arguments in a consistent manner. For
example, the following procedure allows both unnamed and named argument usage.```clojure
(server/defprocedure subtract
["minuend" "subtrahend"]
(server/respond (- (first args) (second args))))
```*Note:* The body automatically receives the variables `args` and `notification`.
`args` is a vector of supplied arguments, matching the order specified in the
`named-args` vector supplied when the procedure was defined. `notification` is a
boolean value that is true when the client does not want to receive a reply with
the result of the procedure call. If the procedure does not respect the
`notification` variable, the server will still not send the result anyway.*Note:* Normally, the number of unnamed arguments supplied must match the number
of named arguments supplied in the procedure definition, but to allow for
variable arguments, the special named argument `"..."` can be used. For example:```clojure
(server/defprocedure add
["a" "..."]
(server/respond (reduce + args)))
```The client must still provide at least the number of named arguments other than
`"..."`. In the previous example, at least one number would have to be specified.The `subtract` method can be called with either of the following JSON:
```javascript
// Unnamed arguments
{
"jsonrpc": "2.0",
"method": "subtract",
"params": [42, 23],
"id": 1
}
``````javascript
// Named arguments
{
"jsonrpc": "2.0",
"method": "subtract",
"params": {
"subtrahend": 23,
"minuend": 42
},
"id": 1
}
```And both would return the same result:
```javascript
{
"jsonrpc": "2.0",
"result": "19",
"id": 1
}
```The server can then be started in one of two ways:
```clojure
; This version blocks the calling thread, so it won't close.
(defn -main
[& args]
...
(server/start [port] [backlog] [bind-addr]))
``````clojure
; This version allows the calling thread to continue immediately, meaning that
; if the main thread closes, the server will also be shut down.
(defn -main
[& args]
...
(server/start-async [port] [backlog] [bind-addr])
...)
```The only required argument to either form is `port`. `backlog` specifies how many
pending connections should be buffered, and is 50 by default. `bind-addr`
specifies the host address that the server should listen on.## Client Usage
First, require the client:
```clojure
(require '[socket-json-rpc.client :as client :refer (notify)])
```The client is very simple, only exposing one macro - `with-server`, and is used
in the form```clojure
(client/with-server [host] [port] [body])
```This form allows the client to call functions on the server *almost* as if they
were local functions. For example, our `subtract` procedure defined above could
be called on a local server running on port `9001` by either:Using the ordered argument form:
```clojure
(client/with-server "localhost" 9001
(subtract 42 23))
```Or, using the named argument form:
```clojure
(client/with-server "localhost" 9001
(subtract :subtrahend 23 :minuend 42))
```Both forms would return the same result:
```clojure
[(false 19)]
````with-server` returns a vector of function results. Each item in the vector is a
list containing```clojure
'([error]
[result] || [code] [message])
; i.e. successful function call
'(false 19); i.e. error condition
'(true -32601 "Method not found")
```A remote procedure call can be wrapped in `notify` if you aren't interested in
receiving a reply with the result of the function call.Multiple functions can be grouped together into a single "batch" by calling them
successively inside the `with-server` block. Note that each function will be
called on the server in-order.```clojure
(client/with-server "localhost" 9001
(subtract 42 23)
(add 1 2 3 4 5)
(subtract :subtrahend 42 :minuend 23)
(notify (add 15 15))
(doesntexist true))
; => [(false 19)
; (false 15)
; (false -19)
; (true -32601 "Method not found")]
```Notice that the return values arrive in the same order as the calls were
delivered.## Common Problems
```clojure
ClassCastException java.lang.String cannot be cast to clojure.lang.Associative
```This usually means that you forgot to add `(respond [val])` around your procedure's
return value.## License
Copyright © 2017 Carl Albrecht
Distributed under the Eclipse Public License version 1.0