jrm1

package module
v0.3.4 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Feb 8, 2024 License: GPL-3.0 Imports: 20 Imported by: 7

README

JSON-RPC M1

A remote procedure call framework written in Go programming language.

Yes, this is a framework, not a library, while it does not allow to automate the process of creation of an RPC infrastructure. Unfortunately, Go language does not fully support dynamic typing. It would be possible to automate the process of encoding and decoding of requests and responses, but it would need to use the "Reflection" feature of the language, which works about a hundred times slower than a normal code.

This framework helps in implementing an RPC server and RPC client using the JSON-RPC M1 protocol. More information about the JSON-RPC M1 protocol can be found in its repository: https://github.com/vault-thirteen/JSON-RPC-M1-Specification

Usage Example

A very simple usage example is available in the example\simple folder of this repository. A description and more information can be found there.

Features

The framework offers several features useful in practice.

  • Settings of this framework are configurable. For example, you can set your own HTTP client using TLS, etc.
  • The RPC server is able to catch and log exceptions (called "panic" in Go language).
  • The framework can count the requests.
  • The framework can measure time taken to perform function calls on the server side.
  • The framework allows user's function to see an ID of a request.
  • The framework allows to set additional meta information in request and response.
  • The framework uses a simple and robust protocol, which is focused on data safety and reliability.
  • The framework is very simple and does not require external tools.

As opposed to many other RPC protocols, this framework has some limits, which are the result of its simplicity.

  • One-side messages are forbidden.
    • Every request must be "acknowledged" with a response.
    • If you need an RPC for game servers, use the UDP protocol and do not cry when someone de-synchs.
  • Batch function calls are forbidden for safety reasons.
    • If you need to call for several functions, make several function calls.
  • The client makes one request at a time.
    • If you need to send spam to the server, use something else.
  • Error codes are not compatible with Google's JSON RPC and XML RPC protocols.
    • We are not Google.
  • This framework is not going to be as fast as GRPC with Protocol Buffers.
    • As with all the protocols using JSON format, textual format is always slower than a binary one.

Documentation

Index

Constants

View Source
const (
	ErrDuplicateFunction  = "duplicate function"
	ErrFunctionIsNotFound = "function is not found"
)
View Source
const (
	ErrEnableExceptionCaptureToLogThem = "enable exception capture to log them"
	ErrMetaDataFieldNameConflict       = "meta data field name conflict"
)
View Source
const (
	ErrFDuplicateMetaDataField  = "duplicate meta data field: %v"
	ErrFMetaDataFieldIsNotFound = "meta data field is not found: %v"
)
View Source
const (
	ErrUserGeneratedErrorsUseAnotherConstructor = "user-generated errors use another constructor"
	ErrUserGeneratedErrorsHaveSpecialCodes      = "user-generated errors have special codes"
)
View Source
const (
	RpcErrorCode_RequestIsNotReadable = -1
	RpcErrorCode_InvalidRequest       = -2
	RpcErrorCode_UnsupportedProtocol  = -4
	RpcErrorCode_UnknownMethod        = -8
	RpcErrorCode_InvalidParameters    = -16
	RpcErrorCode_InternalRpcError     = -32
	//
	RpcErrorCode_ReservedForFuture_1 = -64
	RpcErrorCode_ReservedForFuture_2 = -128
	RpcErrorCode_ReservedForFuture_3 = -256

	// User generated error codes.
	RpcErrorCode_UGEC_Minimal = 1
)

RPC error codes.

View Source
const (
	ErrUnsupportedErrorCode = "unsupported error code"
	ErrFUnknownErrorCode    = "unknown error code: %v"
)
View Source
const (
	RpcErrorMsg_RequestIsNotReadable = "Request is not readable"
	RpcErrorMsg_InvalidRequest       = "Invalid request"
	RpcErrorMsg_UnsupportedProtocol  = "Unsupported protocol"
	RpcErrorMsg_UnknownMethod        = "Unknown method"
	RpcErrorMsg_InvalidParameters    = "Invalid parameters"
	RpcErrorMsg_InternalRpcError     = "Internal RPC error"
	//
	RpcErrorMsg_ReservedForFuture_1 = "Reserved for future (1)"
	RpcErrorMsg_ReservedForFuture_2 = "Reserved for future (2)"
	RpcErrorMsg_ReservedForFuture_3 = "Reserved for future (3)"

	RpcErrorMsg_Empty = ""
)

RPC error messages.

View Source
const (
	ErrFBadSymbolInFunctionName         = "bad symbol in function name: %v"
	ErrFUnsupportedFormatOfFunctionName = "unsupported format of function name: %v"
)
View Source
const (
	ErrFUnsupportedProtocol  = "unsupported protocol: %v"
	ErrRpcRequestIsMalformed = "RPC request is malformed"
)
View Source
const AsciiLowLine = '_'
View Source
const (
	ErrClientSettingsError = "error is client settings"
)
View Source
const (
	ErrErrorMessageIsNotSet = "error message is not set"
)
View Source
const ProtocolNameM1 = "M1"

Variables

This section is empty.

Functions

func CheckFunctionName

func CheckFunctionName(fn string) (err error)

CheckFunctionName verifies name of a function.

Types

type Client

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

Client is an RPC client.

func NewClient

func NewClient(settings *ClientSettings) (c *Client, err error)

NewClient creates an RPC client.

func (*Client) Call

func (c *Client) Call(ctx context.Context, method string, params any, result any) (re *RpcError, err error)

Call performs a function call and puts result into the 'result' argument. The 'result' argument must be a pointer to an initialised (empty) object.

func (*Client) CallRaw

func (c *Client) CallRaw(ctx context.Context, rpcReq *RpcRequest) (rpcResp *RpcResponseRaw, err error)

CallRaw takes a raw request, performs the request, returns a raw response.

func (*Client) GetRequestsCount

func (c *Client) GetRequestsCount() (requestsCount string)

GetRequestsCount returns the counter of performed calls (requests) to the RPC server.

type ClientSettings

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

ClientSettings are settings of an RPC client.

func NewClientSettings

func NewClientSettings(schema string, host string, port uint16, path string, customHttpClient *http.Client, customHttpHeaders map[string]string, useHtmlEscaping bool) (cs *ClientSettings, err error)

NewClientSettings is a constructor of an RPC client settings.

func (*ClientSettings) Check

func (cs *ClientSettings) Check() (err error)

Check validates the settings of an RPC client.

type Processor

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

Processor is an RPC processor (server).

func NewProcessor

func NewProcessor(settings *ProcessorSettings) (p *Processor, err error)

NewProcessor is a constructor of an empty RPC processor (server).

func (*Processor) AddFunc

func (p *Processor) AddFunc(f RpcFunction) (err error)

AddFunc tries to add a function to the RPC processor (server).

func (*Processor) AddFuncFast

func (p *Processor) AddFuncFast(f RpcFunction)

AddFuncFast tries to add a function to the RPC processor (server). It panics on error.

func (*Processor) FindFunc

func (p *Processor) FindFunc(funcName string) (err error)

FindFunc checks presence of the function in the RPC processor (server).

func (*Processor) GetRequestsCount

func (p *Processor) GetRequestsCount() (all, successful string)

GetRequestsCount returns the number of all (received) and successful function calls.

func (*Processor) RemoveFunc

func (p *Processor) RemoveFunc(funcName string) (err error)

RemoveFunc tries to remove a function from the RPC processor (server).

func (*Processor) RunFunc

func (p *Processor) RunFunc(funcName string, params *json.RawMessage, metaData *ResponseMetaData) (result any, re *RpcError)

RunFunc executes a function of the RPC processor (server) specified by its name. If enabled in settings, it also catches any exception (panic) which may happen during the function execution.

func (*Processor) ServeHTTP

func (p *Processor) ServeHTTP(rw http.ResponseWriter, req *http.Request)

ServeHTTP handles an HTTP request and responds to it. 'ServeHTTP' is a required method of the 'http.Handler' interface.

type ProcessorSettings

type ProcessorSettings struct {
	// When enabled, RPC processor (server) will catch exceptions.
	CatchExceptions bool

	// When enabled, RPC processor (server) will journal exceptions.
	LogExceptions bool

	// When enabled, RPC processor (server) will count requests.
	CountRequests bool

	// Name of a meta-data field where to store request duration.
	// When enabled, RPC processor (server) will measure time taken to run
	// functions. Duration is shown only for successful function calls.
	// To enable this feature, set the field name as non-null value.
	DurationFieldName *string

	// Name of a meta-data field where to store ID of the current request.
	// When enabled, RPC processor (server) will add ID of the current request
	// as a meta-data field, so that user function will be able to read it.
	// This field is automatically removed when function call finishes.
	// To enable this feature, set the field name as non-null value.
	RequestIdFieldName *string
}

ProcessorSettings are settings of the RPC processor (server).

func (*ProcessorSettings) Check

func (ps *ProcessorSettings) Check() (err error)

Check verifies processor's settings.

type ResponseMetaData

type ResponseMetaData map[string]any

ResponseMetaData is a set of named fields containing meta information about an RPC function call. It can be useful for transmission of identifier of RPC request, time measurement and many other things not directly related to an RPC function call.

func (*ResponseMetaData) AddField

func (md *ResponseMetaData) AddField(key string, value any) (err error)

AddField adds a field to the set.

func (*ResponseMetaData) AddFieldFast

func (md *ResponseMetaData) AddFieldFast(key string, value any)

AddFieldFast adds a field to the set and panics on error.

func (*ResponseMetaData) GetField

func (md *ResponseMetaData) GetField(key string) (value any)

GetField reads a field of the set.

func (*ResponseMetaData) RemoveField

func (md *ResponseMetaData) RemoveField(key string) (err error)

RemoveField deletes a field from the set.

type RpcError

type RpcError struct {
	// Error code.
	Code RpcErrorCode `json:"code"`

	// Error message.
	Message RpcErrorMessage `json:"message"`

	// Some additional details about an error.
	Data any `json:"data"`
}

RpcError is an RPC error.

func NewRpcError

func NewRpcError(code int, data any) (re *RpcError, err error)

NewRpcError is a common constructor of an RPC error. It returns an error in a Go-language style. This constructor is not for user-generated errors.

func NewRpcErrorByUser

func NewRpcErrorByUser(code int, message string, data any) (re *RpcError)

NewRpcErrorByUser is a constructor of an RPC error created by user. This function throws an exception on error.

func NewRpcErrorFast

func NewRpcErrorFast(code int) (re *RpcError)

NewRpcErrorFast is a fast constructor which throws an exception on error. This constructor should only be used if you know what it is.

func ParseParameters

func ParseParameters(params *json.RawMessage, dst any) (re *RpcError)

ParseParameters parses JSON bytes into an object representing parameters of called RPC method (function, procedure). This function must be called at the beginning of each RPC function to get parameters. Unfortunately, Go language can not do it automatically due to its technical limits.

func (*RpcError) AsError added in v0.3.0

func (re *RpcError) AsError() *RpcErrorStd

AsError returns an RPC error as a standard error.

type RpcErrorCode

type RpcErrorCode int

RpcErrorCode is a code of an RPC error.

func (RpcErrorCode) Check

func (rec RpcErrorCode) Check() (err error)

Check ensures that error code is a valid error code generated by an RPC server. This check refuses error codes having special values for user-generated errors.

func (RpcErrorCode) Int added in v0.2.1

func (rec RpcErrorCode) Int() int

Int returns RPC error code as an integer number.

func (RpcErrorCode) IsGeneratedByUser

func (rec RpcErrorCode) IsGeneratedByUser() bool

IsGeneratedByUser checks whether the error code is a special value used by user-generated errors.

type RpcErrorMessage

type RpcErrorMessage string

RpcErrorMessage is a message for an RPC error. It stores some textual description of an error.

func (RpcErrorMessage) Check

func (rem RpcErrorMessage) Check() (err error)

Check performs a basic check – it ensures that message is set, i.e. is not empty. This function does not validate correctness of the text.

func (RpcErrorMessage) String added in v0.2.0

func (rem RpcErrorMessage) String() string

String returns RPC error message as a string.

type RpcErrorStd added in v0.3.0

type RpcErrorStd struct {
	RpcError
}

RpcErrorStd is a clone of an RPC error which supports methods of the standard error interface.

func NewRpcErrorStd added in v0.3.0

func NewRpcErrorStd(code RpcErrorCode, msg RpcErrorMessage, data any) (res *RpcErrorStd)

func (*RpcErrorStd) Error added in v0.3.0

func (res *RpcErrorStd) Error() string

Error is a standard method of the error interface.

type RpcFunction

type RpcFunction func(params *json.RawMessage, metaData *ResponseMetaData) (result any, re *RpcError)

RpcFunction represents a signature for an RPC function (method, procedure).

func (RpcFunction) GetName

func (f RpcFunction) GetName() string

GetName reads name of the RPC function (method, procedure).

Do note that names of anonymous functions are not usable in practice while Go language has an unstable API and ABI.

Go internal ABI specification is described at the following link: https://go.googlesource.com/go/+/refs/heads/dev.regabi/src/cmd/compile/internal-abi.md

This document clearly states that ABI of Go language is unstable. ----------------------------------------------------------------------------- This ABI is unstable and will change between Go versions. If you’re writing assembly code, please instead refer to Go’s assembly documentation, which describes Go’s stable ABI, known as ABI0. -----------------------------------------------------------------------------

type RpcHttpRequest

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

RpcHttpRequest is an RPC request originated from an HTTP request.

func NewRpcHttpRequest

func NewRpcHttpRequest(p *Processor, settings *ProcessorSettings, req *http.Request, rw http.ResponseWriter) (rhr *RpcHttpRequest)

NewRpcHttpRequest is a simple constructor of an RPC request originated from an HTTP request.

type RpcRequest

type RpcRequest struct {
	// RPC protocol name.
	ProtocolName *string `json:"jsonrpc"`

	// Identifier of request.
	Id *string `json:"id"`

	// Name of the requested RPC function (method, procedure).
	Method *string `json:"method"`

	// Arguments for the requested RPC function (method, procedure).
	Parameters *json.RawMessage `json:"params"`
}

RpcRequest is a raw RPC request. Parameters in this raw request are not parsed.

func NewRpcRequest

func NewRpcRequest(input io.ReadCloser) (rr *RpcRequest, err error)

NewRpcRequest is a constructor of a raw RPC request. It takes an input stream of bytes and decodes it using JSON format.

func (*RpcRequest) CheckProtocolVersion

func (r *RpcRequest) CheckProtocolVersion() (err error)

CheckProtocolVersion tells if the protocol version is correct.

func (*RpcRequest) HasAllRootFields

func (r *RpcRequest) HasAllRootFields() bool

HasAllRootFields tells if all the root fields are set, i.e. are not null pointers.

type RpcResponse

type RpcResponse struct {
	// RPC protocol name.
	ProtocolName string `json:"jsonrpc"`

	// Identifier of request and its response.
	Id *string `json:"id"`

	// Result returned by the called RPC function (method, procedure).
	Result any `json:"result"`

	// Error returned by the called RPC function (method, procedure).
	Error *RpcError `json:"error"`

	// Additional meta-information about the RPC call. This information is not
	// returned by the called function (method, procedure), however the called
	// function (method, procedure) has an interface to interact with this
	// information. It is provided for some specific purposes, such as
	// debugging, logging, transmission of various environment settings, not
	// directly related to the RPC function (method, procedure). The most
	// evident usage of this object is providing an ability to see an ID of the
	// RPC request inside the called RPC function (method, procedure). Another
	// usage example is storing duration of the RPC call, which can be
	// automatically performed by this framework.
	Meta *ResponseMetaData `json:"meta,omitempty"`

	// Flag of success.
	// This field serves at least two purposes. First, it provides additional
	// robustness via redundancy. Second, it allows to check for success not
	// having to parse the whole result JSON object. If you are familiar with
	// the HTTP status code 200, you will understand the idea.
	OK bool `json:"ok"`
}

RpcResponse is an RPC response.

func NewRpcResponse

func NewRpcResponse() (resp *RpcResponse)

NewRpcResponse creates an empty (template) RPC response. Do note that the returned response is not valid until it is filled with actual data.

type RpcResponseRaw

type RpcResponseRaw struct {
	// RPC protocol name.
	ProtocolName string `json:"jsonrpc"`

	// Identifier of request and its response.
	Id *string `json:"id"`

	// Result returned by the called RPC function (method, procedure).
	Result *json.RawMessage `json:"result"`

	// Error returned by the called RPC function (method, procedure).
	Error *RpcError `json:"error"`

	// Additional meta-information about the RPC call. This information is not
	// returned by the called function (method, procedure), however the called
	// function (method, procedure) has an interface to interact with this
	// information. It is provided for some specific purposes, such as
	// debugging, logging, transmission of various environment settings, not
	// directly related to the RPC function (method, procedure). The most
	// evident usage of this object is providing an ability to see an ID of the
	// RPC request inside the called RPC function (method, procedure). Another
	// usage example is storing duration of the RPC call, which can be
	// automatically performed by this framework.
	Meta *ResponseMetaData `json:"meta,omitempty"`

	// Flag of success.
	// This field serves at least two purposes. First, it provides additional
	// robustness via redundancy. Second, it allows to check for success not
	// having to parse the whole result JSON object. If you are familiar with
	// the HTTP status code 200, you will understand the idea.
	OK bool `json:"ok"`
}

RpcResponseRaw is a raw RPC response.

Directories

Path Synopsis
example

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL