README

JSON-RPC 2 Go supporting library

Documentation license donate Download

The library aims to bring JSON-RPC 2.0 support to Go. Goals:

  • Type safe by code-generation
  • Reasonable good performance
  • Clean, extendable and easy-to use interface
  • Protocol-agnostic solution with adapters for common-cases (HTTP, TCP, etc...)

Installation

  • (recommended) look at releases page and download
  • build from source go get -v github.com/reddec/jsonrpc2/cmd/...
  • From bintray repository for most debian-based distribution (trusty, xenial, bionic, buster, wheezy):
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 379CE192D401AB61
echo "deb https://dl.bintray.com/reddec/debian {distribution} main" | sudo tee -a /etc/apt/sources.list
sudo apt install jsonrpc2

Build requirements

  • go 1.13+

Usage as library

Please see package documentation

Usage as CLI for type-safe generation

Usage:
  jsonrpc2-gen [OPTIONS]

Generate tiny wrapper for JSON-RPC router
Author: Baryshnikov Aleksandr <dev@baryshnikov.net>
Version: dev

Application Options:
  -i, --file=                                File to scan [$GOFILE]
  -I, --interface=                           Interface to wrap [$INTERFACE]
      --namespace=                           Custom namespace for functions. If not defined - interface name will be used [$NAMESPACE]
  -w, --wrapper=                             Wrapper function name. If not defined - Register<interface> name will be used [$WRAPPER]
  -o, --output=                              Generated output destination (- means STDOUT) (default: -) [$OUTPUT]
  -p, --package=                             Package name (can be override by output dir) (default: events) [$PACKAGE]
  -d, --doc=                                 Generate markdown documentation [$DOC]
  -c, --case=[keep|camel|pascal|snake|kebab] Method name case style (default: keep) [$CASE]
      --url=                                 URL for examples in documentation (default: https://example.com/api) [$URL]
  -C, --interceptor                          add interceptor for each method [$INTERCEPTOR]

Help Options:
  -h, --help                                 Show this help message

Example

Assume you have an interface file (user.go) like this:

package abc

// General user profile access
type User interface {
	// Get user profile
	Profile(token string) (*Profile, error)
}

Just invoke jsonrpc2-gen -i user.go -o user_gen.go -I User -p abc

You will get user_gen.go file like that:

// Code generated by jsonrpc2-gen. DO NOT EDIT.
//go:generate jsonrpc2-gen -i user.go -o user_gen.go -I User -p abc
package abc

import (
	"encoding/json"
	jsonrpc2 "github.com/reddec/jsonrpc2"
)

func RegisterUser(router *jsonrpc2.Router, wrap User) []string {
	router.RegisterFunc("User.Profile", func(params json.RawMessage, positional bool) (interface{}, error) {
		var args struct {
			Arg0 string `json:"token"`
		}
		var err error
		if positional {
			err = jsonrpc2.UnmarshalArray(params, &args.Arg0)
		} else {
			err = json.Unmarshal(params, &args)
		}
		if err != nil {
			return nil, err
		}
		return wrap.Profile(args.Arg0)
	})

	return []string{"User.Profile"}
}
Generate documentation

Add -doc user.md to generate documentations as described bellow. It will be generated and saved to the provided file (user.md)

# User

General user profile access


## User.Profile

Get user profile

* Method: `User.Profile`
* Returns: `*Profile`
* Arguments:

| Position | Name | Type |
|----------|------|------|
| 0 | token | `string` |

KTOR (kotlin) generator

  • Supports time/Time
  • (TBD) time/Duration

Gradle requirements


def ktor_version = '1.3.1'

repositories {
    mavenCentral()
}

dependencies {
    implementation("io.ktor:ktor-client-cio:$ktor_version") // <-- you can choose another
    implementation("io.ktor:ktor-client-gson:$ktor_version")
}

Expand ▾ Collapse ▴

Documentation

Overview

JSON-RPC 2.0 supporting library

Main object - Router doesn't need any kind of initialization. Just use `var router Router`.

There are two ways how to register service: dynamical and static.

Static

This is recommended way. jsonrpc2-gen tool will generate type-safe wrapper with positional and named arguments support.

Tool can be installed by `go get -v github.com/reddec/jsonrpc2/cmd/...` or by other method (see README.md)

For example:

Assume you have an interface file (`user.go`) like this:

package abc

type User interface {
	Profile(token string) (*Profile, error)
}

Just invoke `jsonrpc2-gen -i user.go -o user_gen.go -I User -p abc`

You will get `user_gen.go` file like that:

// Code generated by jsonrpc2-gen. DO NOT EDIT.
//go:generate jsonrpc2-gen -i user.go -o user_gen.go -I User -p abc
package abc

import (
	"encoding/json"
	jsonrpc2 "github.com/reddec/jsonrpc2"
)

func RegisterUser(router *jsonrpc2.Router, wrap User) []string {
	router.RegisterFunc("User.Profile", func(params json.RawMessage, positional bool) (interface{}, error) {
		var args struct {
			Arg0 string `json:"token"`
		}
		var err error
		if positional {
			err = jsonrpc2.UnmarshalArray(params, &args.Arg0)
		} else {
			err = json.Unmarshal(params, &args)
		}
		if err != nil {
			return nil, err
		}
		return wrap.Profile(args.Arg0)
	})

	return []string{"User.Profile"}
}

Dynamic

By using RegisterPositionalOnly or RegisterNamedOnly. This two functions are heavily relying on reflection so don't use in a high-load environment.

HTTP expose

Helper `Handler` can expose JSON-RPC over HTTP with supported methods POST, PUT, PATCH. For other methods server will return MethodNotAllowed (405)

Index

Examples

Constants

View Source
const (
	Version        = "2.0"
	AppError       = -30000
	ParseError     = -32700
	InvalidRequest = -32600
	MethodNotFound = -32601
	InvalidParams  = -32602
	InternalError  = -32603
)

Variables

This section is empty.

Functions

func Function

func Function(handler interface{}) (*callableWrapper, error)

    Wrap function as JSON-RPC method for usage in router

    This kind of wrapper support only positional arguments

    func Handler

    func Handler(router *Router) http.HandlerFunc

      Expose JSON-RPC route over HTTP Rest (POST) and web sockets (GET)

      Example
      Output:
      
      

      func HandlerContext

      func HandlerContext(ctx context.Context, router *Router) http.HandlerFunc

      func HandlerRest

      func HandlerRest(router *Router) http.HandlerFunc

        Expose JSON-RPC router as HTTP handler where one requests is one execution. Supported methods: POST, PUT, PATCH Expose JSON-RPC router as HTTP handler where one requests is one execution. Supported methods: POST, PUT, PATCH

        func HandlerRestContext

        func HandlerRestContext(ctx context.Context, router *Router) http.HandlerFunc

        func HandlerWS

        func HandlerWS(router *Router) http.HandlerFunc

          Process requests over web socket (all requests are processing in parallel in a separate go-routine)

          func HandlerWSContext

          func HandlerWSContext(ctx context.Context, router *Router) http.HandlerFunc

          func RPCLike

          func RPCLike(handler interface{}) (*rpcLikeCallable, error)

            Expose function handler where first argument is pointer to structure and returns are payload with error.

            This kind of wrapper support only named arguments

            func ToArray

            func ToArray(params json.RawMessage, expected int) ([]json.RawMessage, error)

            func UnmarshalArray

            func UnmarshalArray(params json.RawMessage, args ...interface{}) error

            Types

            type Error

            type Error struct {
            	Code    int         `json:"code"`
            	Message string      `json:"message"`
            	Data    interface{} `json:"data,omitempty"`
            }

              JSON-RPC 2.0 standard error object

              func (*Error) Error

              func (e *Error) Error() string

              type GlobalInterceptorContext

              type GlobalInterceptorContext struct {
              	Requests []*Request
              	Context  context.Context
              	IsBatch  bool
              	// contains filtered or unexported fields
              }

              func (*GlobalInterceptorContext) Next

              func (gic *GlobalInterceptorContext) Next() (responses []*Response, isBatch bool)

              type GlobalInterceptorFunc

              type GlobalInterceptorFunc func(gic *GlobalInterceptorContext) (responses []*Response, isBatch bool)

              func MaxBatch

              func MaxBatch(num int) GlobalInterceptorFunc

                Interceptor that limiting maximum number of requests in a batch. If batch size is bigger - InternalError will be returned with description. Only one error response without ID will be generated regardless of batch size

                type Method

                type Method interface {
                	JsonCall(ctx context.Context, params json.RawMessage, positional bool) (interface{}, error)
                }

                  Method handler (for low-level implementation). Should support params as object or as array (positional=true).

                  Returned data should be JSON serializable and not nil for success

                  type MethodFunc

                  type MethodFunc func(ctx context.Context, params json.RawMessage, positional bool) (interface{}, error)

                  func (MethodFunc) JsonCall

                  func (m MethodFunc) JsonCall(ctx context.Context, params json.RawMessage, positional bool) (interface{}, error)

                  type MethodInterceptorContext

                  type MethodInterceptorContext struct {
                  	Request      *Request
                  	IsPositional bool
                  	Context      context.Context
                  	// contains filtered or unexported fields
                  }

                    Context handling request per method

                    func (*MethodInterceptorContext) Next

                    func (ic *MethodInterceptorContext) Next() (interface{}, error)

                      Call next interceptor or final method

                      type MethodInterceptorFunc

                      type MethodInterceptorFunc func(ic *MethodInterceptorContext) (interface{}, error)

                        Interceptor for each method that will be called

                        type Request

                        type Request struct {
                        	// always 2.0 (will refuse if not)
                        	Version string `json:"jsonrpc"`
                        	// case-sensitive method name
                        	Method string `json:"method"`
                        	// any kind of valid JSON as ID (more relaxed comparing to for spec)
                        	ID json.RawMessage `json:"id"`
                        	// array (for positional) or object (for named) of arguments
                        	Params json.RawMessage `json:"params"`
                        }

                          Standard JSON-RPC 2.0 request messages

                          func (*Request) IsNotification

                          func (rq *Request) IsNotification() bool

                            Check request is notification (null ID)

                            func (*Request) IsValid

                            func (rq *Request) IsValid() bool

                              Base checks against specification

                              type Response

                              type Response struct {
                              	// always 2.0
                              	Version string `json:"jsonrpc"`
                              	// any kind of valid JSON as ID (more relaxed comparing to for spec) copied from request
                              	ID json.RawMessage `json:"id"`
                              	// result if exists
                              	Result interface{} `json:"result,omitempty"`
                              	// error if exists
                              	Error *Error `json:"error,omitempty"`
                              }

                                JSON-RPC 2.0 standard response object

                                type Router

                                type Router struct {
                                	// contains filtered or unexported fields
                                }

                                  Router for JSON-RPC requests.

                                  Supports batching.

                                  func (*Router) Intercept

                                  func (caller *Router) Intercept(handler GlobalInterceptorFunc) *Router

                                    Add interceptor for handling batch before lookup for methods and execution

                                    func (*Router) InterceptMethods

                                    func (caller *Router) InterceptMethods(handler MethodInterceptorFunc) *Router

                                      Add interceptor for handling all methods invoke. Called in a same thread as method

                                      func (*Router) Invoke

                                      func (caller *Router) Invoke(stream io.Reader) (responses []*Response, isBatch bool)

                                        Invoke exposed method using request from stream (as a batch or single) with background context

                                        func (*Router) InvokeContext

                                        func (caller *Router) InvokeContext(ctx context.Context, stream io.Reader) (responses []*Response, isBatch bool)

                                          Invoke exposed method using request from stream (as a batch or single) with custom context

                                          func (*Router) Register

                                          func (caller *Router) Register(method string, handler Method) *Router

                                            Register method to router to expose over JSON-RPC interface

                                            func (*Router) RegisterFunc

                                            func (caller *Router) RegisterFunc(method string, handlerFunc MethodFunc) *Router

                                              Register function as method to expose over JSON-RPC

                                              func (*Router) RegisterNamedOnly

                                              func (caller *Router) RegisterNamedOnly(method string, handler interface{}) error

                                                Register function as exposed method. Function handler must have first argument is pointer to structure and must return payload and error.

                                                This kind of wrapper supports only named arguments.

                                                Example
                                                Output:
                                                
                                                

                                                func (*Router) RegisterPositionalOnly

                                                func (caller *Router) RegisterPositionalOnly(method string, handler interface{}) error

                                                  Register function as exposed method. Handler must return two values, last of them - error.

                                                  For such methods only positional arguments supported.

                                                  Example
                                                  Output:
                                                  
                                                  

                                                  Directories

                                                  Path Synopsis
                                                  cmd