README

zenrpc: JSON-RPC 2.0 Server Implementation with SMD support

Go Report Card Build Status codecov GoDoc

zenrpc is a JSON-RPC 2.0 server library with Service Mapping Description support. It's built on top of go generate instead of reflection.

How to Use

Service is struct with RPC methods, service represents RPC namespace.

  1. Install zenrpc generator go get github.com/semrush/zenrpc/v2/zenrpc
  2. Import github.com/semrush/zenrpc/v2 into our code with rpc service.
  3. Add trailing comment //zenrpc to your service or embed zenrpc.Service into your service struct.
  4. Write your funcs almost as usual.
  5. Do not forget run go generate or zenrpc for magic
Accepted Method Signatures
func(Service) Method([args]) (<value>, <error>)
func(Service) Method([args]) <value>
func(Service) Method([args]) <error>
func(Service) Method([args])
  • Value could be a pointer
  • Error is error or *zenrpc.Error

Example

package main

import (
	"flag"
	"context"
	"errors"
	"math"
	"log"
	"net/http"
	"os"	
	
	"github.com/semrush/zenrpc/v2"
	"github.com/semrush/zenrpc/v2/testdata"
)

type ArithService struct{ zenrpc.Service }

// Sum sums two digits and returns error with error code as result and IP from context.
func (as ArithService) Sum(ctx context.Context, a, b int) (bool, *zenrpc.Error) {
	r, _ := zenrpc.RequestFromContext(ctx)

	return true, zenrpc.NewStringError(a+b, r.Host)
}

// Multiply multiples two digits and returns result.
func (as ArithService) Multiply(a, b int) int {
	return a * b
}

type Quotient struct {
	Quo, Rem int
}

func (as ArithService) Divide(a, b int) (quo *Quotient, err error) {
	if b == 0 {
		return nil, errors.New("divide by zero")
	} else if b == 1 {
		return nil, zenrpc.NewError(401, errors.New("we do not serve 1"))
	}

	return &Quotient{
		Quo: a / b,
		Rem: a % b,
	}, nil
}

// Pow returns x**y, the base-x exponential of y. If Exp is not set then default value is 2.
//zenrpc:exp=2
func (as ArithService) Pow(base float64, exp float64) float64 {
	return math.Pow(base, exp)
}

//go:generate zenrpc

func main() {
	addr := flag.String("addr", "localhost:9999", "listen address")
	flag.Parse()

	rpc := zenrpc.NewServer(zenrpc.Options{ExposeSMD: true})
	rpc.Register("arith", testdata.ArithService{})
	rpc.Register("", testdata.ArithService{}) // public
	rpc.Use(zenrpc.Logger(log.New(os.Stderr, "", log.LstdFlags)))

	http.Handle("/", rpc)

	log.Printf("starting arithsrv on %s", *addr)
	log.Fatal(http.ListenAndServe(*addr, nil))
}

Magic comments

All comments are optional.

Method comments
//zenrpc:<method parameter>[=<default value>][whitespaces<description>]
//zenrpc:<error code>[whitespaces<description>]
//zenrpc:return[whitespaces<description>]
 
Struct comments
type MyService struct {} //zenrpc

JSON-RPC 2.0 Supported Features

  • Requests
    • Single requests
    • Batch requests
    • Notifications
  • Parameters
    • Named
    • Position
    • Default values
  • SMD Schema
    • Input
    • Output
    • Codes
    • Scopes for OAuth

Server Library Features

  • go generate
  • Transports
    • HTTP
    • WebSocket
    • RabbitMQ
  • Server middleware
    • Basic support
    • Metrics
    • Logging
Expand ▾ Collapse ▴

Documentation

Index

Constants

View Source
const (
	// ParseError is error code defined by JSON-RPC 2.0 spec.
	// Invalid JSON was received by the server.
	// An error occurred on the server while parsing the JSON text.
	ParseError = -32700

	// InvalidRequest is error code defined by JSON-RPC 2.0 spec.
	// The JSON sent is not as valid Request object.
	InvalidRequest = -32600

	// MethodNotFound is error code defined by JSON-RPC 2.0 spec.
	// The method does not exist / is not available.
	MethodNotFound = -32601

	// InvalidParams is error code defined by JSON-RPC 2.0 spec.
	// Invalid method parameter(s).
	InvalidParams = -32602

	// InternalError is error code defined by JSON-RPC 2.0 spec.
	// Internal JSON-RPC error.
	InternalError = -32603

	// ServerError is error code defined by JSON-RPC 2.0 spec.
	// Reserved for implementation-defined server-errors.
	ServerError = -32000

	// Version is only supported JSON-RPC Version.
	Version = "2.0"
)
View Source
const (

	// context key for ID.
	IDKey contextKey = "id"
)

Variables

This section is empty.

Functions

func ConvertToObject

func ConvertToObject(keys []string, params json.RawMessage) (json.RawMessage, error)

    ConvertToObject converts json array into object using key by index from keys array.

    func ErrorMsg

    func ErrorMsg(code int) string

      ErrorMsg returns error as text for default JSON-RPC errors.

      func IDFromContext

      func IDFromContext(ctx context.Context) *json.RawMessage

        IDFromContext returns request ID from context.

        func IsArray

        func IsArray(message json.RawMessage) bool

          IsArray checks json message if it array or object.

          func NamespaceFromContext

          func NamespaceFromContext(ctx context.Context) string

            NamespaceFromContext returns method's namespace from context.

            func RequestFromContext

            func RequestFromContext(ctx context.Context) (*http.Request, bool)

              RequestFromContext returns http.Request from context.

              func SMDBoxHandler

              func SMDBoxHandler(w http.ResponseWriter, r *http.Request)

                SMDBoxHandler is a handler for SMDBox web app.

                Types

                type Error

                type Error struct {
                	// A Number that indicates the error type that occurred.
                	// This MUST be an integer.
                	Code int `json:"code"`
                
                	// A String providing as short description of the error.
                	// The message SHOULD be limited to as concise single sentence.
                	Message string `json:"message"`
                
                	// A Primitive or Structured value that contains additional information about the error.
                	// This may be omitted.
                	// The value of this member is defined by the Server (e.g. detailed error information, nested errors etc.).
                	Data interface{} `json:"data,omitempty"`
                
                	// Err is inner error.
                	Err error `json:"-"`
                }

                  Error object used in response if function call errored. See: http://www.jsonrpc.org/specification#error_object

                  func NewError

                  func NewError(code int, err error) *Error

                    NewError makes a JSON-RPC error with given code and standard error.

                    func NewStringError

                    func NewStringError(code int, message string) *Error

                      NewStringError makes a JSON-RPC with given code and message.

                      func (Error) Error

                      func (e Error) Error() string

                        Error returns first filled value from Err, Message or default text for JSON-RPC error.

                        type InvokeFunc

                        type InvokeFunc func(context.Context, string, json.RawMessage) Response

                          InvokeFunc is a function for processing single JSON-RPC 2.0 Request after validation and parsing.

                          type Invoker

                          type Invoker interface {
                          	Invoke(ctx context.Context, method string, params json.RawMessage) Response
                          	SMD() smd.ServiceInfo
                          }

                            Invoker implements service handler.

                            type MiddlewareFunc

                            type MiddlewareFunc func(InvokeFunc) InvokeFunc

                              MiddlewareFunc is a function for executing as middleware.

                              func Logger

                              func Logger(l *log.Logger) MiddlewareFunc

                                Logger is middleware for JSON-RPC 2.0 Server. It's just an example for middleware, will be refactored later.

                                func Metrics

                                func Metrics(appName string) MiddlewareFunc

                                  Metrics is a middleware for logging duration of RPC requests via Prometheus. Default AppName is zenrpc. It exposes two metrics: appName_rpc_error_requests_count and appName_rpc_responses_duration_seconds.

                                  type Options

                                  type Options struct {
                                  	// BatchMaxLen sets maximum quantity of requests in single batch.
                                  	BatchMaxLen int
                                  
                                  	// TargetURL is RPC endpoint.
                                  	TargetURL string
                                  
                                  	// ExposeSMD exposes SMD schema with ?smd GET parameter.
                                  	ExposeSMD bool
                                  
                                  	// DisableTransportChecks disables Content-Type and methods checks. Use only for development mode.
                                  	DisableTransportChecks bool
                                  
                                  	// AllowCORS adds header Access-Control-Allow-Origin with *.
                                  	AllowCORS bool
                                  
                                  	// Upgrader sets options for gorilla websocket. If nil, default options will be used
                                  	Upgrader *websocket.Upgrader
                                  
                                  	// HideErrorDataField removes data field from response error
                                  	HideErrorDataField bool
                                  }

                                    Options is options for JSON-RPC 2.0 Server.

                                    type Printer

                                    type Printer interface {
                                    	Printf(string, ...interface{})
                                    }

                                    type Request

                                    type Request struct {
                                    	// A String specifying the version of the JSON-RPC protocol. MUST be exactly "2.0".
                                    	Version string `json:"jsonrpc"`
                                    
                                    	// An identifier established by the Client that MUST contain as String, Number, or NULL value if included.
                                    	// If it is not included it is assumed to be as notification.
                                    	// The value SHOULD normally not be Null [1] and Numbers SHOULD NOT contain fractional parts.
                                    	ID *json.RawMessage `json:"id"`
                                    
                                    	// A String containing the name of the method to be invoked.
                                    	// Method names that begin with the word rpc followed by as period character (U+002E or ASCII 46)
                                    	// are reserved for rpc-internal methods and extensions and MUST NOT be used for anything else.
                                    	Method string `json:"method"`
                                    
                                    	// A Structured value that holds the parameter values to be used during the invocation of the method.
                                    	// This member MAY be omitted.
                                    	Params json.RawMessage `json:"params"`
                                    
                                    	// Namespace holds namespace. Not in spec, for internal needs.
                                    	Namespace string `json:"-"`
                                    }

                                      Request is a json structure for json-rpc request to server. See: http://www.jsonrpc.org/specification#request_object

                                      type Response

                                      type Response struct {
                                      	// A String specifying the version of the JSON-RPC protocol. MUST be exactly "2.0".
                                      	Version string `json:"jsonrpc"`
                                      
                                      	// This member is REQUIRED.
                                      	// It MUST be the same as the value of the id member in the Request Object.
                                      	// If there was an error in detecting the id in the Request object (e.g. Parse error/Invalid Request), it MUST be Null.
                                      	ID *json.RawMessage `json:"id"`
                                      
                                      	// This member is REQUIRED on success.
                                      	// This member MUST NOT exist if there was an error invoking the method.
                                      	// The value of this member is determined by the method invoked on the Server.
                                      	Result *json.RawMessage `json:"result,omitempty"`
                                      
                                      	// This member is REQUIRED on error.
                                      	// This member MUST NOT exist if there was no error triggered during invocation.
                                      	// The value for this member MUST be an Object as defined in section 5.1.
                                      	Error *Error `json:"error,omitempty"`
                                      
                                      	// Extensions is additional field for extending standard response. It could be useful for tracing, method execution, etc...
                                      	Extensions map[string]interface{} `json:"extensions,omitempty"`
                                      }

                                        Response is json structure for json-rpc response from server. See: http://www.jsonrpc.org/specification#response_object

                                        func NewResponseError

                                        func NewResponseError(id *json.RawMessage, code int, message string, data interface{}) Response

                                          NewResponseError returns new Response with Error object.

                                          func (Response) JSON

                                          func (r Response) JSON() []byte

                                            JSON is temporary method that silences error during json marshalling.

                                            func (*Response) Set

                                            func (r *Response) Set(v interface{}, er ...error)

                                              Set sets result and error if needed.

                                              type Server

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

                                                Server is JSON-RPC 2.0 Server.

                                                func NewServer

                                                func NewServer(opts Options) Server

                                                  NewServer returns new JSON-RPC 2.0 Server.

                                                  func (Server) Do

                                                  func (s Server) Do(ctx context.Context, req []byte) ([]byte, error)

                                                    Do process JSON-RPC 2.0 request, invokes correct method for namespace and returns JSON-RPC 2.0 Response or marshaller error.

                                                    func (*Server) Register

                                                    func (s *Server) Register(namespace string, service Invoker)

                                                      Register registers new service for given namespace. For public namespace use empty string.

                                                      func (*Server) RegisterAll

                                                      func (s *Server) RegisterAll(services map[string]Invoker)

                                                        RegisterAll registers all services listed in map.

                                                        func (Server) SMD

                                                        func (s Server) SMD() smd.Schema

                                                          SMD returns Service Mapping Description object with all registered methods.

                                                          func (Server) ServeHTTP

                                                          func (s Server) ServeHTTP(w http.ResponseWriter, r *http.Request)

                                                            ServeHTTP process JSON-RPC 2.0 requests via HTTP. http://www.simple-is-better.org/json-rpc/transport_http.html

                                                            func (Server) ServeWS

                                                            func (s Server) ServeWS(w http.ResponseWriter, r *http.Request)

                                                              ServeWS processes JSON-RPC 2.0 requests via Gorilla WebSocket. https://github.com/gorilla/websocket/blob/master/examples/echo/

                                                              func (*Server) SetLogger

                                                              func (s *Server) SetLogger(printer Printer)

                                                                SetLogger sets logger for debug

                                                                func (*Server) Use

                                                                func (s *Server) Use(m ...MiddlewareFunc)

                                                                  Use registers middleware.

                                                                  type Service

                                                                  type Service struct{}

                                                                    Service is as struct for discovering JSON-RPC 2.0 services for zenrpc generator cmd.

                                                                    Directories

                                                                    Path Synopsis