jsoff

package module
v0.6.4 Latest Latest
Warning

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

Go to latest
Published: May 27, 2024 License: MIT Imports: 14 Imported by: 7

README

jsoff

jsoff is a JSONRPC 2.0 library and a suite of client utilities in golang

features

  • Full spec support
  • JSON schema checking
  • Support multiple network transports, http1, http2, websocket, tcp and vsockets(for virtual machines and hypervisors)
  • Utility command line tools to call JSON RPC servers, see bin/jsonrpc-*

Install and build

% git checkout https://github.com/superisaac/jsoff
% cd jsoff
% make test # run unit test
...
% make build  # build cli tools
go build -gcflags=-G=3 -o bin/jsonrpc-call cli/call/jsonrpc_call.go
go build -gcflags=-G=3 -o bin/jsonrpc-notify cli/notify/jsonrpc_notify.go
go build -gcflags=-G=3 -o bin/jsonrpc-watch cli/watch/jsonrpc_watch.go
go build -gcflags=-G=3 -o bin/jsoff-example-fifo examples/fifo/main.go

Examples

Start a simple JSONRPC server

server side code

import (
    "github.com/superisaac/jsoff/net"
)
// create a HTTP/1 handler, currently http1, http2 and websocket handlers are supported
server := jsoffnet.NewHttp1Handler(nil)
// register an actor function
server.Actor.OnTyped("echo", func(req *jsoffnet.RPCRequest, text string) (string, error) {
    return "echo " + text, nil
})
// serve the JSONRPC at port 8000
jsoffnet.ListenAndServe(rootCtx, ":8000", server)

the server can be tested using client tools jsonrpc-call

% bin/jsonrpc-call -c http://127.0.0.1:8000 echo hi
{
   "jsonrpc": "2.0",
   "id": "1",
   "result": "echo hi"
}

Initialize a JSONRPC request

import (
    "context"
    "github.com/superisaac/jsoff"
    "github.com/superisaac/jsoff/net"
)

// create a jsonrpc client according to the server url
// the supported url schemes are: http, https, h2, h2c, ws and wss
client := jsoffnet.NewClient("http://127.0.0.1:8000")

// create a request message with a random id field
reqmsg := jsoff.NewRequestMessage(jsoff.NewUuid(), "echo", []interface{}{"hi5"})
fmt.Printf("request message: %s\n", jsoff.MessageString(reqmsg))

resmsg, err := client.Call(context.Background(), reqmsg)
fmt.Printf("result message: %s\n", jsoff.MessageString(resmsg))
assert.True(resmsg.IsResultOrError())  // resmsg is a Result type message or an Error type message
assert.Equal("echo hi5", resmsg.MustResult())

// a notify message, notify message doesn't have id field and doesn't expect result
ntfmsg := jsoff.NewNotifyMessage("echo", []interface{}{"hi6"})
err := client.Send(context.Background(), ntfmsg)

FIFO service

the FIFO service is an example to demonstrate how jsoff server and client works without writing and code. the server maintains an array in memory, you can push/pop/get items from it and list all items, you can even subscribe the item additions.

Start server which listen at port 6000
% bin/jsoff-example-fifo
INFO[0000] Example fifo service starts at 127.0.0.1:6000
Open another terminal and type a sequence of commands
# list the fifo and get an empty list
% bin/jsonrpc-call -c http://127.0.0.1:6000 fifo_list
{
  "jsonrpc": "2.0",
  "id": "a85bfe31c94f4a5bb0fcb6539bbd6d66",
  "result": []
}

# push an item "hello"
 % bin/jsonrpc-call -c http://127.0.0.1:6000 fifo_push hello
{
  "jsonrpc": "2.0",
  "id": "c460ebe3a9094249a043b6cddf3fa29f",
  "result": "ok"
}

# call list again, now that the item "hello" is pushed 
% bin/jsonrpc-call -c http://127.0.0.1:6000 fifo_list
{
  "jsonrpc": "2.0",
  "id": "b3262cc3166e45e2bb5e58939d5e73bb",
  "result": [
    "hello"
  ]
}

# push another item 5(integer)
% bin/jsonrpc-call -c http://127.0.0.1:6000 fifo_push 5
{
  "jsonrpc": "2.0",
  "id": "dc2bbac79e6841d78786e5ff5fc37c13",
  "result": "ok"
}

# now there are 2 items
% bin/jsonrpc-call -c http://127.0.0.1:6000 fifo_list
{
  "jsonrpc": "2.0",
  "id": "29ca7b80ac504e9c9dd3513f3d4b966d",
  "result": [
    "hello",
    5
  ]
}

# get fifo[1], which is the second item of fifo
% bin/jsonrpc-call -c http://127.0.0.1:6000 fifo_get 1
{
  "jsonrpc": "2.0",
  "id": "1c3fd5fe30034b72b115705263961c22",
  "result": 5
}

# pop an item out of the fifo
% bin/jsonrpc-call -c http://127.0.0.1:6000 fifo_pop
{
  "jsonrpc": "2.0",
  "id": "df4bbe13c79c4fdc914ed8961ced9cf3",
  "result": "ok"
}

# the item 5 was removed
% bin/jsonrpc-call -c http://127.0.0.1:6000 fifo_list
{
  "jsonrpc": "2.0",
  "id": "f767a5275f544b9db2ba091cdebd9f5f",
  "result": [
    "hello"
  ]
}

Open another terminal to subscribe item pushing

% bin/jsonrpc-watch -c h2c://127.0.0.1:6000 fifo_subscribe
{
  "jsonrpc": "2.0",
  "id": "abdc63d3873649a1a7a2b1bd49916e44",
  "result": "ok"
}

Note that the cli command is bin/jsonrpc-watch and the server url scheme is h2c:// which means the client can be in streaming mode, ws:// is also streaming schema but the http1 client doesn't support streaming.

now switch to the second terminal and push another item, the output turn out to be showed in the third terminal.

 % bin/jsonrpc-watch -c h2c://127.0.0.1:6000 fifo_subscribe
{
  "jsonrpc": "2.0",
  "id": "abdc63d3873649a1a7a2b1bd49916e44",
  "result": "ok"
}
{
  "jsonrpc": "2.0",
  "method": "fifo_subscription",
  "params": [
    "world"
  ]
}

Documentation

Overview

jsoff is JSONRPC 2.0 libaray in golang

Index

Constants

View Source
const (
	MKRequest = iota
	MKNotify
	MKResult
	MKError
)

message kinds

Variables

View Source
var (
	ErrServerError = &RPCError{100, "server error", nil}

	ErrMethodNotFound = &RPCError{-32601, "method not found", nil}

	ErrEmptyMethod = &RPCError{-32601, "empty method", nil}

	ErrParseMessage   = &RPCError{-32700, "parse error", nil}
	ErrInvalidRequest = &RPCError{-32600, "invalid request", nil}

	ErrInternalError = &RPCError{-32603, "internal error", nil}

	ErrMessageType = &RPCError{105, "wrong message type", nil}

	ErrTimeout     = &RPCError{200, "request timeout", nil}
	ErrBadResource = &RPCError{201, "bad resource", nil}
	ErrLiveExit    = &RPCError{202, "live exit", nil}

	ErrNotAllowed = &RPCError{406, "type not allowed", nil}
	ErrAuthFailed = &RPCError{401, "auth failed", nil}

	ErrInvalidSchema = &RPCError{-32633, "invalid schema", nil}
)

error defination https://www.jsonrpc.org/specification#error_object

Functions

func DecodeInterface

func DecodeInterface(input interface{}, output interface{}) error

func DecodeParams

func DecodeParams(params []interface{}, outputPtr interface{}) error

/ Convert params to a struct field by field

func EncodePretty

func EncodePretty(msg Message) (string, error)

Message methods

func ErrorResponse

func ErrorResponse(w http.ResponseWriter, r *http.Request, err error, status int, message string)

func GuessJson

func GuessJson(input string) (interface{}, error)

func GuessJsonArray

func GuessJsonArray(inputArr []string) ([]interface{}, error)

func IsMethod

func IsMethod(name string) bool

method names

func IsPublicMethod

func IsPublicMethod(name string) bool

func MarshalJson

func MarshalJson(data interface{}) (string, error)

func MessageBytes

func MessageBytes(msg Message) ([]byte, error)

func MessageMap

func MessageMap(msg Message) (map[string]interface{}, error)

func MessageString

func MessageString(msg Message) string

func MustMessageBytes added in v0.5.8

func MustMessageBytes(msg Message) []byte

func NewUuid

func NewUuid() string

Types

type BaseMessage

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

The base class of JSONRPC types

func (BaseMessage) IsError

func (self BaseMessage) IsError() bool

IsError() returns if the message is a ErrorMessage

func (BaseMessage) IsNotify

func (self BaseMessage) IsNotify() bool

IsNotify() returns if the message is a NotifyMessage

func (BaseMessage) IsRequest

func (self BaseMessage) IsRequest() bool

IsRequest() returns if the message is a RequestMessage

func (BaseMessage) IsResponse

func (self BaseMessage) IsResponse() bool

IsResponse() returns if the message is a RequestMessage or NotifyMessage

func (BaseMessage) IsResult

func (self BaseMessage) IsResult() bool

IsResult() returns if the message is a ResultMessage

func (BaseMessage) IsResultOrError

func (self BaseMessage) IsResultOrError() bool

IsResultOrError() returns if the message is a ResultMessage or ErrorMessage

func (*BaseMessage) SetTraceId

func (self *BaseMessage) SetTraceId(traceId string)

func (BaseMessage) TraceId

func (self BaseMessage) TraceId() string

type Bigint

type Bigint big.Int

func (*Bigint) Load

func (self *Bigint) Load(repr string)

func (Bigint) MarshalJSON

func (self Bigint) MarshalJSON() ([]byte, error)

func (*Bigint) String

func (self *Bigint) String() string

func (*Bigint) UnmarshalJSON

func (self *Bigint) UnmarshalJSON(data []byte) error

func (*Bigint) Value

func (self *Bigint) Value() *big.Int

type ErrorMessage

type ErrorMessage struct {
	BaseMessage
	Id    interface{}
	Error *RPCError
	// contains filtered or unexported fields
}

Error message kind

func NewErrorMessage

func NewErrorMessage(reqmsg Message, errbody *RPCError) *ErrorMessage

func NewErrorMessageFromId

func NewErrorMessageFromId(reqId interface{}, traceId string, errbody *RPCError) *ErrorMessage

func RPCErrorMessage

func RPCErrorMessage(reqmsg Message, code int, message string, data interface{}) *ErrorMessage

func RPCErrorMessageFromId

func RPCErrorMessageFromId(reqId interface{}, traceId string, code int, message string, data interface{}) *ErrorMessage

func (ErrorMessage) HasResponseHeader added in v0.6.0

func (self ErrorMessage) HasResponseHeader() bool

func (*ErrorMessage) Interface

func (self *ErrorMessage) Interface() interface{}

func (ErrorMessage) Log

func (self ErrorMessage) Log() *log.Entry

func (ErrorMessage) MustError

func (self ErrorMessage) MustError() *RPCError

func (ErrorMessage) MustId

func (self ErrorMessage) MustId() interface{}

func (ErrorMessage) MustMethod

func (self ErrorMessage) MustMethod() string

func (ErrorMessage) MustParams

func (self ErrorMessage) MustParams() []interface{}

func (ErrorMessage) MustResult

func (self ErrorMessage) MustResult() interface{}

func (ErrorMessage) ReplaceId

func (self ErrorMessage) ReplaceId(newId interface{}) Message

func (*ErrorMessage) ResponseHeader added in v0.6.0

func (self *ErrorMessage) ResponseHeader() http.Header

type Message

type Message interface {
	// Return's the judgement of message types
	IsRequest() bool
	IsNotify() bool
	IsResponse() bool
	IsResult() bool
	IsError() bool
	IsResultOrError() bool

	// TraceId can be used to analyse the flow of whole message
	// transportation
	SetTraceId(traceId string)
	TraceId() string

	// Returns template structures, this structure can be used to
	// marshal and turn into map
	Interface() interface{}

	// MustId returns the Id field of a message, will panic when
	// message is a Notify
	MustId() interface{}

	// MustMethod returns the method of a message, will panic when
	// message is an Result or Error.
	MustMethod() string

	// MustParams returns the params of a message, will panic when
	// message is a Result or Error
	MustParams() []interface{}

	// MustResult returns the result field of a message, will
	// panic when the message is not a Result
	MustResult() interface{}

	// MustError returns the error field of a message, will panic
	// when the message is not an Error
	MustError() *RPCError

	// Clone the message with a new Id
	ReplaceId(interface{}) Message

	// Log returns a Logger object with message specific
	// infomations attached.
	Log() *log.Entry
}

The abstract interface of JSONRPC message. refer to https://www.jsonrpc.org/specification

data Message = Request id method params |

Notify method params |
Result id result |
Error id error={ code message data }

func DecodeMessage

func DecodeMessage(decoder *json.Decoder) (Message, error)

func ParseBytes

func ParseBytes(data []byte) (Message, error)

type NotifyMessage

type NotifyMessage struct {
	BaseMessage
	Method string
	Params []interface{}
	// contains filtered or unexported fields
}

Notify message kind

func NewNotifyMessage

func NewNotifyMessage(method string, params interface{}) *NotifyMessage

func (*NotifyMessage) Interface

func (self *NotifyMessage) Interface() interface{}

func (NotifyMessage) Log

func (self NotifyMessage) Log() *log.Entry

func (NotifyMessage) MustError

func (self NotifyMessage) MustError() *RPCError

func (NotifyMessage) MustId

func (self NotifyMessage) MustId() interface{}

func (NotifyMessage) MustMethod

func (self NotifyMessage) MustMethod() string

func (NotifyMessage) MustParams

func (self NotifyMessage) MustParams() []interface{}

func (NotifyMessage) MustResult

func (self NotifyMessage) MustResult() interface{}

func (NotifyMessage) ReplaceId

func (self NotifyMessage) ReplaceId(newId interface{}) Message

type RPCError

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

RPC error object

func NewErrMsgType

func NewErrMsgType(additional string) *RPCError

Create a new instance of ErrMessageType additional is the information to help identify error details

func ParamsError

func ParamsError(message string) *RPCError

func (*RPCError) Error

func (self *RPCError) Error() string

String representation of RPCError object

func (RPCError) ToMessage

func (self RPCError) ToMessage(reqmsg *RequestMessage) *ErrorMessage

Convert RPCError to ErrorMessage, reqmsg is the original RequestMessage instance, the ErrorMessage will copy reqmsg's id property.

func (RPCError) ToMessageFromId

func (self RPCError) ToMessageFromId(reqId interface{}, traceId string) *ErrorMessage

Convert RPCError to ErrorMessage, reqId and traceId can be used to compose the result error message

func (*RPCError) WithData

func (self *RPCError) WithData(data interface{}) *RPCError

WithData create clone this RPCError object with data attached

type RequestMessage

type RequestMessage struct {
	BaseMessage
	Id     interface{}
	Method string
	Params []interface{}
	// contains filtered or unexported fields
}

Request message kind

func NewRequestMessage

func NewRequestMessage(id interface{}, method string, params interface{}) *RequestMessage

func (RequestMessage) CacheKey

func (self RequestMessage) CacheKey(prefix string) string

func (RequestMessage) Clone

func (self RequestMessage) Clone(newId interface{}) *RequestMessage

func (*RequestMessage) Interface

func (self *RequestMessage) Interface() interface{}

Interface

func (RequestMessage) Log

func (self RequestMessage) Log() *log.Entry

Log

func (RequestMessage) MustError

func (self RequestMessage) MustError() *RPCError

MustError

func (RequestMessage) MustId

func (self RequestMessage) MustId() interface{}

MustId

func (RequestMessage) MustMethod

func (self RequestMessage) MustMethod() string

MustMethod

func (RequestMessage) MustParams

func (self RequestMessage) MustParams() []interface{}

MustParams

func (RequestMessage) MustResult

func (self RequestMessage) MustResult() interface{}

MustResult

func (RequestMessage) ReplaceId

func (self RequestMessage) ReplaceId(newId interface{}) Message

type ResponseMessage added in v0.6.0

type ResponseMessage interface {
	HasResponseHeader() bool
	ResponseHeader() http.Header
}

type ResultMessage

type ResultMessage struct {
	BaseMessage
	Id     interface{}
	Result interface{}
	// contains filtered or unexported fields
}

Result message kind

func NewResultMessage

func NewResultMessage(reqmsg Message, result interface{}) *ResultMessage

func (ResultMessage) HasResponseHeader added in v0.6.0

func (self ResultMessage) HasResponseHeader() bool

implements ResponseMessage

func (*ResultMessage) Interface

func (self *ResultMessage) Interface() interface{}

func (ResultMessage) Log

func (self ResultMessage) Log() *log.Entry

func (ResultMessage) MustError

func (self ResultMessage) MustError() *RPCError

func (ResultMessage) MustId

func (self ResultMessage) MustId() interface{}

func (ResultMessage) MustMethod

func (self ResultMessage) MustMethod() string

func (ResultMessage) MustParams

func (self ResultMessage) MustParams() []interface{}

func (ResultMessage) MustResult

func (self ResultMessage) MustResult() interface{}

func (ResultMessage) ReplaceId

func (self ResultMessage) ReplaceId(newId interface{}) Message

func (*ResultMessage) ResponseHeader added in v0.6.0

func (self *ResultMessage) ResponseHeader() http.Header

type UID

type UID string

Directories

Path Synopsis
cli
examples
interacting jsonrpc in http family specs, currently jsoffnet provides 3 mechanisms: the classical http/1.1, websocket and http/2 wire protocol.
interacting jsonrpc in http family specs, currently jsoffnet provides 3 mechanisms: the classical http/1.1, websocket and http/2 wire protocol.

Jump to

Keyboard shortcuts

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