JSON-RPC 2.0 client+server implementation in pure V.
For both Request/Response constructors are provided and must be used for initialization.
import net.jsonrpc
// jsonrpc.new_request(method, params, id)
mut req := jsonrpc.new_request('kv.create', {
'key': 'key'
'value': 'value'
}, 'kv.create.1')
println(req.encode())
// '{"jsonrpc":"2.0","method":"kv.create","params":{"key":"key","value":"value"},"id":"kv.create.1"}'
// jsonrpc.new_response(result, error, id)
mut resp := jsonrpc.new_response({
'key': 'key'
'value': 'value'
}, jsonrpc.ResponseError{}, 'kv.create.1')
println(resp.encode())
// '{"jsonrpc":"2.0","result":{"key":"key","value":"value"},"id":"kv.create.1"}'
To create a Notification, pass empty string as Request.id (jsonrpc.Empty{}.str() or
jsonrpc.empty.str() can be used)
(e.g. jsonrpc.new_reponse('method', 'params', jsonrpc.empty.str())).
To omit Response.params in encoded json string pass jsonrpc.Empty{} or jsonrpc.empty as
value in constructor (e.g. jsonrpc.new_reponse('method', jsonrpc.empty, 'id')).
For Response only result or error fields can exist at the same time and not both simultaniously.
If error passed to jsonrpc.new_response() the result value will be ignored on Response.encode().
The error field is not generated if jsonrpc.ResponseError{} provided as error into
jsonrpc.new_response() (e.g. jsonrpc.new_response("result", jsonrpc.ResponseError{}, "id")).
If the empty string passed as Result.id it will use jsonrpc.null as id (translates to json null)
For full usage check client in example
import net
import net.jsonrpc
addr := '127.0.0.1:42228'
mut stream := net.dial_tcp(addr)!
mut c := jsonrpc.new_client(jsonrpc.ClientConfig{
stream: stream
})
c.notify('kv.create', {
'key': 'bazz'
'value': 'barr'
})!
Client can work with any io.ReaderWriter provided into stream field value.
For ready key/value im-memory storage realized with server check this example
import net
import net.jsonrpc
fn handle_test(req &jsonrpc.Request, mut wr jsonrpc.ResponseWriter) {
p := req.decode_params[string]() or {
wr.write_error(jsonrpc.invalid_params)
return
}
wr.write(p)
}
fn handle_conn(mut conn net.TcpConn) {
defer { conn.close() or {} }
mut srv := jsonrpc.new_server(jsonrpc.ServerConfig{
stream: conn
handler: handle_test
})
srv.start()
}
addr := '127.0.0.1:42228'
mut l := net.listen_tcp(.ip, addr)!
println('TCP JSON-RPC server on ${addr} (Content-Length framing)')
for {
mut c := l.accept()!
println('Accepted')
go handle_conn(mut c)
}
Server can work with any io.ReaderWriter provided into stream field value.
Server requires jsonrpc.Handler = fn(req &jsonrpc.Request, mut wr jsonrpc.ResponseWriter)
to pass decoded jsonrpc.Request and to write jsonrpc.Response into jsonrpc.ResponseWriter.
On Notification Server does call jsonrpc.Handler but it ingores written jsonrpc.Response.
jsonrpc.Handler = fn(req &jsonrpc.Request, mut wr jsonrpc.ResponseWriter) is the function that
operates the decoded jsonrpc.Request and writes jsonrpc.Response into jsonrpc.ResponseWriter.
Before every return wr.write() or wr.write_error() must be called so the server do not stuck
waiting for jsonrpc.Response to be written. Also only wr.write() or wr.write_error() must
be called before return and not both.
The simple jsonrpc.Router is provided to register jsonrpc.Handler to handle specific method.
The jsonrpc.Router.handle_jsonrpc must be passed into jsonrpc.Server.handler to handle requests.
If jsonrpc.Request.method has no registered jsonrpc.Handler, the router will respond
with jsonrpc.method_not_found error
Both jsonrpc.Client and jsonrpc.Server support jsonrpc.Interceptors - the collection of
on event handlers. There is implementation of all supported interceptors called
jsonrpc.LoggingInterceptor.