jsonrpc

package module
v2.4.2 Latest Latest
Warning

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

Go to latest
Published: Oct 9, 2022 License: MIT Imports: 10 Imported by: 1

README

jsonrpc

GitHub Actions codecov Go Report Card codebeat badge Maintainability GoDoc GitHub license

About

Install

$ go get github.com/osamingo/jsonrpc/v2@latest

Usage

package main

import (
	"context"
	"log"
	"net/http"

	"github.com/goccy/go-json"
	"github.com/osamingo/jsonrpc/v2"
)

type (
	EchoHandler struct{}
	EchoParams  struct {
		Name string `json:"name"`
	}
	EchoResult struct {
		Message string `json:"message"`
	}

	PositionalHandler struct{}
	PositionalParams  []int
	PositionalResult  struct {
		Message []int `json:"message"`
	}
)

func (h EchoHandler) ServeJSONRPC(c context.Context, params *json.RawMessage) (any, *jsonrpc.Error) {

	var p EchoParams
	if err := jsonrpc.Unmarshal(params, &p); err != nil {
		return nil, err
	}

	return EchoResult{
		Message: "Hello, " + p.Name,
	}, nil
}

func (h PositionalHandler) ServeJSONRPC(c context.Context, params *json.RawMessage) (any, **jsonrpc.Error) {

	var p PositionalParams
	if err := jsonrpc.Unmarshal(params, &p); err != nil {
		return nil, err
	}

	return PositionalResult{
		Message: p,
	}, nil
}

func main() {

	mr := jsonrpc.NewMethodRepository()

	if err := mr.RegisterMethod("Main.Echo", EchoHandler{}, EchoParams{}, EchoResult{}); err != nil {
		log.Fatalln(err)
	}

	if err := mr.RegisterMethod("Main.Positional", PositionalHandler{}, PositionalParams{}, PositionalResult{}); err != nil {
		log.Fatalln(err)
	}

	http.Handle("/jrpc", mr)
	http.HandleFunc("/jrpc/debug", mr.ServeDebug)

	if err := http.ListenAndServe(":8080", http.DefaultServeMux); err != nil {
		log.Fatalln(err)
	}
}
Advanced
package main

import (
	"log"
	"net/http"

	"github.com/osamingo/jsonrpc/v2"
)

type (
	HandleParamsResulter interface {
		jsonrpc.Handler
		Name() string
		Params() any
		Result() any
	}
	Servicer interface {
		MethodName(HandleParamsResulter) string
		Handlers() []HandleParamsResulter
	}
	UserService struct {
		SignUpHandler HandleParamsResulter
		SignInHandler HandleParamsResulter
	}
)

func (us *UserService) MethodName(h HandleParamsResulter) string {
	return "UserService." + h.Name()
}

func (us *UserService) Handlers() []HandleParamsResulter {
	return []HandleParamsResulter{us.SignUpHandler, us.SignInHandler}
}

func NewUserService() *UserService {
	return &UserService{
	// Initialize handlers
	}
}

func main() {

	mr := jsonrpc.NewMethodRepository()

	for _, s := range []Servicer{NewUserService()} {
		for _, h := range s.Handlers() {
			mr.RegisterMethod(s.MethodName(h), h, h.Params(), h.Result())
		}
	}

	http.Handle("/jrpc", mr)
	http.HandleFunc("/jrpc/debug", mr.ServeDebug)

	if err := http.ListenAndServe(":8080", http.DefaultServeMux); err != nil {
		log.Fatalln(err)
	}
}
Result
Invoke the Echo method
POST /jrpc HTTP/1.1
Accept: application/json, */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Content-Length: 82
Content-Type: application/json
Host: localhost:8080
User-Agent: HTTPie/0.9.6

{
  "jsonrpc": "2.0",
  "method": "Main.Echo",
  "params": {
    "name": "John Doe"
  },
  "id": "243a718a-2ebb-4e32-8cc8-210c39e8a14b"
}

HTTP/1.1 200 OK
Content-Length: 68
Content-Type: application/json
Date: Mon, 28 Nov 2016 13:48:13 GMT

{
  "jsonrpc": "2.0",
  "result": {
    "message": "Hello, John Doe"
  },
  "id": "243a718a-2ebb-4e32-8cc8-210c39e8a14b"
}
Invoke the Positional method
POST /jrpc HTTP/1.1
Accept: */*
Content-Length: 133
Content-Type: application/json
Host: localhost:8080
User-Agent: curl/7.61.1

{
  "jsonrpc": "2.0",
  "method": "Main.Positional",
  "params": [3,1,1,3,5,3],
  "id": "243a718a-2ebb-4e32-8cc8-210c39e8a14b"
}

HTTP/1.1 200 OK
Content-Length: 97
Content-Type: application/json
Date: Mon, 05 Nov 2018 11:23:35 GMT

{
  "jsonrpc": "2.0",
  "result": {
    "message": [3,1,1,3,5,3]
  },
  "id": "243a718a-2ebb-4e32-8cc8-210c39e8a14b"
}

Access to debug handler
GET /jrpc/debug HTTP/1.1
Accept: */*
Accept-Encoding: gzip, deflate
Connection: keep-alive
Host: localhost:8080
User-Agent: HTTPie/0.9.6

HTTP/1.1 200 OK
Content-Length: 408
Content-Type: application/json
Date: Mon, 28 Nov 2016 13:56:24 GMT

[
  {
    "handler": "EchoHandler",
    "name": "Main.Echo",
    "params": {
      "$ref": "#/definitions/EchoParams",
      "$schema": "http://json-schema.org/draft-04/schema#",
      "definitions": {
        "EchoParams": {
          "additionalProperties": false,
          "properties": {
            "name": {
              "type": "string"
            }
          },
          "required": [
            "name"
          ],
          "type": "object"
        }
      }
    },
    "result": {
      "$ref": "#/definitions/EchoResult",
      "$schema": "http://json-schema.org/draft-04/schema#",
      "definitions": {
        "EchoResult": {
          "additionalProperties": false,
          "properties": {
            "message": {
              "type": "string"
            }
          },
          "required": [
            "message"
          ],
          "type": "object"
        }
      }
    }
  }
]

License

Released under the MIT License.

Documentation

Overview

Package jsonrpc helps JSON-RPC 2.0 implements.

Index

Examples

Constants

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

Variables

This section is empty.

Functions

func MethodName

func MethodName(c context.Context) string

MethodName takes method name from context.

func ParseRequest

func ParseRequest(r *http.Request) ([]*Request, bool, *Error)

ParseRequest parses a HTTP request to JSON-RPC request.

func RequestID

func RequestID(c context.Context) *json.RawMessage

RequestID takes request id from context.

func SendResponse

func SendResponse(w http.ResponseWriter, resp []*Response, batch bool) error

SendResponse writes JSON-RPC response.

func WithMetadata

func WithMetadata(c context.Context, md Metadata) context.Context

WithMetadata adds jsonrpc metadata to context.

func WithMethodName

func WithMethodName(c context.Context, name string) context.Context

WithMethodName adds method name to context.

func WithRequestID

func WithRequestID(c context.Context, id *json.RawMessage) context.Context

WithRequestID adds request id to context.

Types

type Error

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

An Error is a wrapper for a JSON interface value.

func ErrInternal

func ErrInternal() *Error

ErrInternal returns internal error.

func ErrInvalidParams

func ErrInvalidParams() *Error

ErrInvalidParams returns invalid params error.

func ErrInvalidRequest

func ErrInvalidRequest() *Error

ErrInvalidRequest returns invalid request error.

func ErrMethodNotFound

func ErrMethodNotFound() *Error

ErrMethodNotFound returns method not found error.

func ErrParse

func ErrParse() *Error

ErrParse returns parse error.

func Unmarshal

func Unmarshal(params *json.RawMessage, dst any) *Error

Unmarshal decodes JSON-RPC params.

func (*Error) Error

func (e *Error) Error() string

Error implements error interface.

type ErrorCode

type ErrorCode int

A ErrorCode by JSON-RPC 2.0.

const (
	// ErrorCodeParse is parse error code.
	ErrorCodeParse ErrorCode = -32700
	// ErrorCodeInvalidRequest is invalid request error code.
	ErrorCodeInvalidRequest ErrorCode = -32600
	// ErrorCodeMethodNotFound is method not found error code.
	ErrorCodeMethodNotFound ErrorCode = -32601
	// ErrorCodeInvalidParams is invalid params error code.
	ErrorCodeInvalidParams ErrorCode = -32602
	// ErrorCodeInternal is internal error code.
	ErrorCodeInternal ErrorCode = -32603
)

type Handler

type Handler interface {
	ServeJSONRPC(c context.Context, params *json.RawMessage) (result any, err *Error)
}

Handler links a method of JSON-RPC request.

type HandlerFunc

type HandlerFunc func(c context.Context, params *json.RawMessage) (result any, err *Error)

HandlerFunc type is an adapter to allow the use of ordinary functions as JSONRPC handlers. If f is a function with the appropriate signature, HandlerFunc(f) is a jsonrpc.Handler that calls f.

func (HandlerFunc) ServeJSONRPC

func (f HandlerFunc) ServeJSONRPC(c context.Context, params *json.RawMessage) (any, *Error)

ServeJSONRPC calls f(w, r).

type Metadata

type Metadata struct {
	Handler Handler
	Params  any
	Result  any
}

Metadata has method meta data.

func GetMetadata

func GetMetadata(c context.Context) Metadata

GetMetadata takes jsonrpc metadata from context.

type MethodReference

type MethodReference struct {
	Name    string             `json:"name"`
	Handler string             `json:"handler"`
	Params  *jsonschema.Schema `json:"params,omitempty"`
	Result  *jsonschema.Schema `json:"result,omitempty"`
}

A MethodReference is a reference of JSON-RPC method.

type MethodRepository

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

A MethodRepository has JSON-RPC method functions.

func NewMethodRepository

func NewMethodRepository() *MethodRepository

NewMethodRepository returns new MethodRepository.

func (*MethodRepository) InvokeMethod

func (mr *MethodRepository) InvokeMethod(c context.Context, r *Request) *Response

InvokeMethod invokes JSON-RPC method.

func (*MethodRepository) Methods

func (mr *MethodRepository) Methods() map[string]Metadata

Methods returns registered methods.

func (*MethodRepository) RegisterMethod

func (mr *MethodRepository) RegisterMethod(method string, h Handler, params, result any) error

RegisterMethod registers jsonrpc.Func to MethodRepository.

func (*MethodRepository) ServeDebug

func (mr *MethodRepository) ServeDebug(w http.ResponseWriter, r *http.Request)

ServeDebug views registered method list.

func (*MethodRepository) ServeHTTP

func (mr *MethodRepository) ServeHTTP(w http.ResponseWriter, r *http.Request)

ServeHTTP provides basic JSON-RPC handling.

Example
package main

import (
	"bytes"
	"context"
	"io"
	"log"
	"net/http"
	"net/http/httptest"
	"os"

	"github.com/goccy/go-json"
	"github.com/osamingo/jsonrpc/v2"
)

type (
	EchoHandler struct{}
	EchoParams  struct {
		Name string `json:"name"`
	}
	EchoResult struct {
		Message string `json:"message"`
	}

	PositionalHandler struct{}
	PositionalParams  []int
	PositionalResult  struct {
		Message []int `json:"message"`
	}
)

func (h EchoHandler) ServeJSONRPC(c context.Context, params *json.RawMessage) (interface{}, *jsonrpc.Error) {
	var p EchoParams
	if err := jsonrpc.Unmarshal(params, &p); err != nil {
		return nil, err
	}

	return EchoResult{
		Message: "Hello, " + p.Name,
	}, nil
}

func (h PositionalHandler) ServeJSONRPC(c context.Context, params *json.RawMessage) (interface{}, *jsonrpc.Error) {
	var p PositionalParams
	if err := jsonrpc.Unmarshal(params, &p); err != nil {
		return nil, err
	}

	return PositionalResult{
		Message: p,
	}, nil
}

func main() { //nolint: nosnakecase
	mr := jsonrpc.NewMethodRepository()

	if err := mr.RegisterMethod("Main.Echo", EchoHandler{}, EchoParams{}, EchoResult{}); err != nil {
		log.Println(err)

		return
	}

	if err := mr.RegisterMethod("Main.Positional", PositionalHandler{}, PositionalParams{}, PositionalResult{}); err != nil {
		log.Println(err)

		return
	}

	http.Handle("/jrpc", mr)
	http.HandleFunc("/jrpc/debug", mr.ServeDebug)

	srv := httptest.NewServer(http.DefaultServeMux)
	defer srv.Close()

	contextType := "application/json"
	echoVal := `{
		"jsonrpc": "2.0",
		"method": "Main.Echo",
		"params": {
			"name": "John Doe"
		},
		"id": "243a718a-2ebb-4e32-8cc8-210c39e8a14b"
	}`

	req, err := http.NewRequestWithContext(context.Background(), http.MethodPost, srv.URL+"/jrpc", bytes.NewBufferString(echoVal))
	if err != nil {
		log.Println(err)

		return
	}

	req.Header.Add("Content-Type", contextType)
	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Println(err)

		return
	}
	defer resp.Body.Close()
	if _, err := io.Copy(os.Stdout, resp.Body); err != nil {
		log.Println(err)

		return
	}

	positionalVal := `{
		"jsonrpc": "2.0",
		"method": "Main.Positional",
		"params": [3, 1, 1, 3, 5, 3],
		"id": "243a718a-2ebb-4e32-8cc8-210c39e8a14b"
	}`

	req, err = http.NewRequestWithContext(context.Background(), http.MethodPost, srv.URL+"/jrpc", bytes.NewBufferString(positionalVal))
	if err != nil {
		log.Println(err)

		return
	}

	req.Header.Add("Content-Type", contextType)
	resp, err = http.DefaultClient.Do(req)
	if err != nil {
		log.Println(err)

		return
	}
	defer resp.Body.Close()
	if _, err := io.Copy(os.Stdout, resp.Body); err != nil {
		log.Println(err)

		return
	}

}
Output:

{"jsonrpc":"2.0","result":{"message":"Hello, John Doe"},"id":"243a718a-2ebb-4e32-8cc8-210c39e8a14b"}
{"jsonrpc":"2.0","result":{"message":[3,1,1,3,5,3]},"id":"243a718a-2ebb-4e32-8cc8-210c39e8a14b"}

func (*MethodRepository) TakeMethod

func (mr *MethodRepository) TakeMethod(r *Request) (Handler, *Error)

TakeMethod takes jsonrpc.Func in MethodRepository.

func (*MethodRepository) TakeMethodMetadata

func (mr *MethodRepository) TakeMethodMetadata(r *Request) (Metadata, *Error)

TakeMethodMetadata takes metadata in MethodRepository for request.

type Request

type Request struct {
	Version string           `json:"jsonrpc"`
	Method  string           `json:"method"`
	Params  *json.RawMessage `json:"params"`
	ID      *json.RawMessage `json:"id"`
}

A Request represents a JSON-RPC request received by the server.

type Response

type Response struct {
	Version string           `json:"jsonrpc"`
	Result  any              `json:"result,omitempty"`
	Error   *Error           `json:"error,omitempty"`
	ID      *json.RawMessage `json:"id,omitempty"`
}

A Response represents a JSON-RPC response returned by the server.

func NewResponse

func NewResponse(r *Request) *Response

NewResponse generates a JSON-RPC response.

Jump to

Keyboard shortcuts

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