Documentation ¶
Overview ¶
Package pjrpc contains router and Servers to build JSON-RPC service. Also it contains generator for boilerplate code looks like a gRPC.
Index ¶
- Constants
- Variables
- func ContextGetConnection(ctx context.Context) (conn net.Conn, ok bool)
- func ContextSetConnection(ctx context.Context, conn net.Conn) context.Context
- func ContextSetData(ctx context.Context, data *ContextData) context.Context
- type BatchRequests
- type BatchResponses
- type ContextData
- type ErrorResponse
- func JRPCErrInternalError(data ...any) *ErrorResponse
- func JRPCErrInvalidParams(data ...any) *ErrorResponse
- func JRPCErrInvalidRequest(data ...any) *ErrorResponse
- func JRPCErrMethodNotFound(data ...any) *ErrorResponse
- func JRPCErrParseError(data ...any) *ErrorResponse
- func JRPCErrServerError(code int, data ...any) *ErrorResponse
- type Handler
- type Middleware
- type Registrator
- type Request
- type Response
- type Router
- type Server
- func (*Server) DefaultOnErrorParseRequest(context.Context, error) *ErrorResponse
- func (s *Server) DefaultRestoreOnPanic(_ context.Context, err error) *ErrorResponse
- func (s *Server) RegisterMethod(methodName string, h Handler)
- func (s *Server) Serve(ctx context.Context, body json.RawMessage) json.RawMessage
- func (s *Server) SetLogger(w io.Writer)
- func (s *Server) With(mws ...Middleware)
- type ServerHTTP
- type ServerHTTPOption
- type ServerListener
Examples ¶
Constants ¶
const ( // ContentTypeHeaderName name of the header Content-Type. ContentTypeHeaderName = "Content-Type" // ContentTypeHeaderValue value of the header Content-Type. ContentTypeHeaderValue = "application/json" // JSONRPCVersion is a version of supported JSON-RPC protocol. JSONRPCVersion = "2.0" )
Variables ¶
var ( // ErrPanicInHandler returns in the server when handler called panic. ErrPanicInHandler = errors.New("panic in handler") // ErrBadStatusCode returns in client when http response has code not 200. ErrBadStatusCode = errors.New("bad status code") // ErrWrongContentType returns in client when http response has content-type not application/json. ErrWrongContentType = errors.New("wrong content type") // ErrUnsupportedMods returns in client that are not supported request modifications. ErrUnsupportedMods = errors.New("mods are not supported") // ErrorResponseChannelClosed returns in async client when response listener got closed channel. ErrorResponseChannelClosed = errors.New("response channel is closed") )
Functions ¶
func ContextGetConnection ¶ added in v2.3.0
ContextGetConnection returns websocket connection from context.
func ContextSetConnection ¶ added in v2.3.0
ContextSetConnection sets websocket connection to the context.
func ContextSetData ¶
func ContextSetData(ctx context.Context, data *ContextData) context.Context
ContextSetData sets ContextData to the context.
Types ¶
type BatchRequests ¶
type BatchRequests []*Request
BatchRequests client MAY send an Array filled with Request objects. See spec https://www.jsonrpc.org/specification#batch page.
type BatchResponses ¶
type BatchResponses []*Response
BatchResponses the Server should respond with an Array containing the corresponding Response objects, after all of the batch Request objects have been processed.
type ContextData ¶
type ContextData struct { // It's raw HTTP request from router. HTTPRequest *http.Request // It's parsed body of the request. JRPCRequest *Request // It will be true if this JSON-RPC request is a part of the one request. IsBatch bool }
ContextData router data of the request as context.
func ContextGetData ¶
func ContextGetData(ctx context.Context) (d *ContextData, ok bool)
ContextGetData returns ContextData from the context.
type ErrorResponse ¶
type ErrorResponse struct { // A Number that indicates the error type that occurred. Code int `json:"code"` // A String providing a short description of the error. Message string `json:"message"` // A Primitive or Structured value that contains additional information about the error. Data json.RawMessage `json:"data,omitempty"` }
ErrorResponse model of the response with error.
Example ¶
package main import ( "encoding/json" "fmt" "gitlab.com/pjrpc/pjrpc/v2" ) type customErrorData struct { Code int `json:"code"` Message string `json:"message"` } func main() { // Specification errors with or without your data. jerr := pjrpc.JRPCErrInternalError() fmt.Println(jerr) jerr = pjrpc.JRPCErrInternalError("some text") fmt.Println(jerr) jerr = pjrpc.JRPCErrInternalError(nil) fmt.Println(jerr) data := &customErrorData{ Code: 123, Message: "custom message", } jerr = pjrpc.JRPCErrInternalError(data) fmt.Println(jerr) jsonErr, err := json.Marshal(jerr) if err != nil { return } fmt.Println(string(jsonErr)) // Your custom JSON-RPC error. jerr = pjrpc.JRPCErrServerError(-32001) fmt.Println(jerr) jsonErr, err = json.Marshal(jerr) if err != nil { return } fmt.Println(string(jsonErr)) jerr = pjrpc.JRPCErrServerError(-32002, nil) fmt.Println(jerr) jsonErr, err = json.Marshal(jerr) if err != nil { return } fmt.Println(string(jsonErr)) // Your custom JSON-RPC error with your data. data.Code = 321 jerr = pjrpc.JRPCErrServerError(-32002, data) fmt.Println(jerr) jsonErr, err = json.Marshal(jerr) if err != nil { return } fmt.Println(string(jsonErr)) }
Output: JSON-RPC Error: [-32603] Internal error JSON-RPC Error: [-32603] Internal error ("some text") JSON-RPC Error: [-32603] Internal error (null) JSON-RPC Error: [-32603] Internal error ({"code":123,"message":"custom message"}) {"code":-32603,"message":"Internal error","data":{"code":123,"message":"custom message"}} JSON-RPC Error: [-32001] Server error {"code":-32001,"message":"Server error"} JSON-RPC Error: [-32002] Server error (null) {"code":-32002,"message":"Server error","data":null} JSON-RPC Error: [-32002] Server error ({"code":321,"message":"custom message"}) {"code":-32002,"message":"Server error","data":{"code":321,"message":"custom message"}}
func JRPCErrInternalError ¶
func JRPCErrInternalError(data ...any) *ErrorResponse
JRPCErrInternalError internal JSON-RPC error.
func JRPCErrInvalidParams ¶
func JRPCErrInvalidParams(data ...any) *ErrorResponse
JRPCErrInvalidParams invalid method parameter(s).
func JRPCErrInvalidRequest ¶
func JRPCErrInvalidRequest(data ...any) *ErrorResponse
JRPCErrInvalidRequest the JSON sent is not a valid Request object.
func JRPCErrMethodNotFound ¶
func JRPCErrMethodNotFound(data ...any) *ErrorResponse
JRPCErrMethodNotFound the method does not exist / is not available.
func JRPCErrParseError ¶
func JRPCErrParseError(data ...any) *ErrorResponse
JRPCErrParseError invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.
func JRPCErrServerError ¶
func JRPCErrServerError(code int, data ...any) *ErrorResponse
JRPCErrServerError reserved for implementation-defined server-errors. Codes -32000 to -32099.
func (*ErrorResponse) Error ¶
func (e *ErrorResponse) Error() string
Error implementation error interface.
type Handler ¶
Handler request handler takes request context and raw json params. Returns type with result data and error.
type Middleware ¶
Middleware wraps handler and call before wrapped handler.
type Registrator ¶
type Registrator interface { // RegisterMethod saves method handler to router. RegisterMethod(methodName string, h Handler) // With adds midlewares to the queue. It will be call before methods. With(mws ...Middleware) }
Registrator provides methods to register handler and middlewares.
type Request ¶
type Request struct { // A String specifying the version of the JSON-RPC protocol. MUST be exactly "2.0". JSONRPC string `json:"jsonrpc"` // An identifier established by the Client that MUST contain a String, Number, or NULL value if included. ID json.RawMessage `json:"id,omitempty"` // A String containing the name of the method to be invoked. Method string `json:"method"` // A Structured value that holds the parameter values to be used during the invocation of the method. Params json.RawMessage `json:"params,omitempty"` }
Request model of the JSON-RPC request.
func NewRequest ¶ added in v2.2.0
NewRequest returns new Request object with marshaled ID and Params. You can pass ID as empty string if you want to create a Notification request.
func (*Request) IsNotification ¶ added in v2.2.0
IsNotification returns true when request doesn't have id.
func (*Request) JSON ¶ added in v2.2.0
func (r *Request) JSON() json.RawMessage
JSON converts Request object to JSON message.
type Response ¶
type Response struct { // A String specifying the version of the JSON-RPC protocol. MUST be exactly "2.0". JSONRPC string `json:"jsonrpc"` // It MUST be the same as the value of the id member in the Request. ID json.RawMessage `json:"id,omitempty"` // This member is REQUIRED on success. 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. Error *ErrorResponse `json:"error,omitempty"` }
Response model of the response object.
func NewResponseFromError ¶ added in v2.2.0
NewResponseFromError returns response model based on Error.
func NewResponseFromJSON ¶ added in v2.2.0
func NewResponseFromJSON(msg json.RawMessage) (*Response, error)
NewResponseFromJSON parses json message and returns Response object.
func NewResponseFromJSONReader ¶ added in v2.2.0
NewResponseFromJSONReader reads io.Reader as a JSON data. Returns Response object and parse error.
func NewResponseFromRequest ¶ added in v2.2.0
NewResponseFromRequest returns new response model based on Request.
func (*Response) JSON ¶ added in v2.2.0
func (r *Response) JSON() json.RawMessage
JSON converts response model into JSON text.
func (*Response) UnmarshalResult ¶ added in v2.2.0
UnmarshalResult parses Result field in the Response object to destination param.
type Router ¶
type Router struct {
// contains filtered or unexported fields
}
Router contains storage with handlers and common middleware.
func (*Router) Invoke ¶
func (r *Router) Invoke(ctx context.Context, methodName string, params json.RawMessage) (any, error)
Invoke invokes handler by method name. If the router has middleware, it will be called first. After that the registered middleware will be called and then the registered handler.
func (*Router) MethodWith ¶ added in v2.1.0
func (r *Router) MethodWith(methodName string, mw Middleware) error
MethodWith adds middleware to exclusive method, method must be registered.
func (*Router) RegisterMethod ¶
RegisterMethod saves handler of the method to storage.
func (*Router) With ¶
func (r *Router) With(mws ...Middleware)
With adds middlewares to the router's queue of the middlewares.
type Server ¶ added in v2.2.0
type Server struct { // Contains handlers for JSON-RPC methods. Router *Router // Logger is an optional logger as a last chance to alert about error. // Also writes panics from handlers. Logger *log.Logger // Panic handler calls when your rpc handler make panic. // There will be default panic handler (DefaultRestoreOnPanic). OnPanic func(ctx context.Context, err error) *ErrorResponse // OnErrorParseRequest handler calls when server can't parse client request. // There will be default handler (DefaultOnErrorParseRequest). OnErrorParseRequest func(ctx context.Context, err error) *ErrorResponse }
Server as an protocol agnostic only JSON-RPC specification.
func NewServer ¶ added in v2.2.0
func NewServer() *Server
NewServer returns new protocol agnostic JSON-RPC server.
func (*Server) DefaultOnErrorParseRequest ¶ added in v2.2.0
func (*Server) DefaultOnErrorParseRequest(context.Context, error) *ErrorResponse
DefaultOnErrorParseRequest default handler calls when server can't parse client request. Returns JSON-RPC error Invalid Request with static text.
func (*Server) DefaultRestoreOnPanic ¶ added in v2.2.0
func (s *Server) DefaultRestoreOnPanic(_ context.Context, err error) *ErrorResponse
DefaultRestoreOnPanic default panic handler. It just prints error in log and sets default internal error in response.
func (*Server) RegisterMethod ¶ added in v2.2.0
RegisterMethod saves handler of the method to starage.
func (*Server) Serve ¶ added in v2.2.0
func (s *Server) Serve(ctx context.Context, body json.RawMessage) json.RawMessage
Serve is a main method of the Server. It parses body of the request and builds response. This method never returns error but puts it in the response.
func (*Server) SetLogger ¶ added in v2.2.0
SetLogger sets your io.Writer as an error logger of the pjrpc server. Also you can set your own *log.Logger in Logger field.
func (*Server) With ¶ added in v2.2.0
func (s *Server) With(mws ...Middleware)
With adds middlewares to the router's queue of the middlewares.
type ServerHTTP ¶
ServerHTTP JSON-RPC server over HTTP protocol.
Example ¶
package main import ( "context" "encoding/json" "fmt" "log" "net/http" "net/http/httptest" "strings" "gitlab.com/pjrpc/pjrpc/v2" ) type rpc struct{} type request struct { Name string `json:"name"` } type response struct { Name string `json:"name"` } func middleware(next pjrpc.Handler) pjrpc.Handler { return func(ctx context.Context, params json.RawMessage) (any, error) { fmt.Println("I'm middleware :)") return next(ctx, params) } } func (*rpc) helloHandler(ctx context.Context, params json.RawMessage) (any, error) { req := &request{} err := json.Unmarshal(params, req) if err != nil { return nil, pjrpc.JRPCErrInvalidParams(err.Error()) } fmt.Println("request from:", req.Name) return &response{Name: "server"}, nil } func main() { r := &rpc{} srv := pjrpc.NewServerHTTP() // add your error logger to the server to catch error in write response method and panics. srv.SetLogger(log.Writer()) srv.With(middleware) srv.RegisterMethod("hello", r.helloHandler) mux := http.NewServeMux() mux.Handle("/rpc", srv) // In real life you will start listener of the http server... rec := httptest.NewRecorder() bodyReq := strings.NewReader(`{"jsonrpc":"2.0","id":"1","method":"hello","params":{"name":"client"}}`) req := httptest.NewRequest(http.MethodPost, "/rpc", bodyReq) req.Header.Set("Content-Type", "application/json") mux.ServeHTTP(rec, req) fmt.Println("=== text after request ===") fmt.Println(rec.Code) fmt.Println(rec.Body.String()) }
Output: I'm middleware :) request from: client === text after request === 200 {"jsonrpc":"2.0","id":"1","result":{"name":"server"}}
func NewServerHTTP ¶
func NewServerHTTP(options ...ServerHTTPOption) *ServerHTTP
NewServerHTTP creates new server with default error handlers and empty router.
func (*ServerHTTP) ServeHTTP ¶
func (s *ServerHTTP) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP implements interface of handler in http package.
type ServerHTTPOption ¶ added in v2.5.0
type ServerHTTPOption func(s *ServerHTTP)
ServerHTTPOption options to modify parameters of the HTTP server.
func ServerHTTPOptionMaxBodyLimit ¶ added in v2.5.0
func ServerHTTPOptionMaxBodyLimit(limit int64) ServerHTTPOption
ServerHTTPOptionMaxBodyLimit sets limit of body reader.
type ServerListener ¶ added in v2.3.0
type ServerListener struct { *Server Listener net.Listener // OnErrorAccept takes error after attempt to accept a new connection. // If you don't want to stop the listener you can return nil here. OnErrorAccept func(err error) error // OnNewConnection it called on each new accepted connection. // You can return false here if you want to refuse the connection. OnNewConnection func(ctx context.Context, conn net.Conn) (accept bool) // OnCloseConnection it called before each closure of the connection. // If you refused the connection in the OnNewConnection method, // you will not get that connection here. OnCloseConnection func(ctx context.Context, conn net.Conn) // OnErrorReceive it called when Handler gets reading error from the connection. // By default the connection will be closed. OnErrorReceive func(ctx context.Context, conn net.Conn, err error) (needClose bool) // OnErrorSend it called when Handler gets writing error from the connection. // By default the connection will be closed. OnErrorSend func(ctx context.Context, conn net.Conn, err error) (needClose bool) }
ServerListener JSON-RPC server based on net.Listener. You can use it as JSON-RPC server over TCP or other protocols. Look for "On..." method of the server to set your own callbacks.
Example ¶
listner, err := net.Listen("tcp", ":35396") if err != nil { fmt.Println("net.Listen:", err) return } srv := pjrpc.NewServerListener(listner) srv.RegisterMethod("method", func(ctx context.Context, params json.RawMessage) (any, error) { fmt.Println("request:", string(params)) return "response", nil }) go func() { if errListen := srv.Listen(); errListen != nil { fmt.Println("srv.Listen:", errListen) } }() conn, err := net.Dial("tcp", ":35396") if err != nil { fmt.Println("net.Dial:", err) return } cl := client.NewAsyncClient(conn) go func() { if errListen := cl.Listen(); errListen != nil { fmt.Println("cl.Listen:", errListen) } }() var res string err = cl.Invoke(context.Background(), "1", "method", "params", &res) if err != nil { fmt.Println("cl.Invoke:", err) return } if err = cl.Close(); err != nil { fmt.Println("cl.Close:", err) return } if err = srv.Close(); err != nil { fmt.Println("cl.Close:", err) return } fmt.Println("response:", res)
Output: request: "params" response: response
func NewServerListener ¶ added in v2.3.0
func NewServerListener(listener net.Listener) *ServerListener
NewServerListener returns a new Server with default handler's callbacks.
func (*ServerListener) Close ¶ added in v2.3.0
func (s *ServerListener) Close() error
Close closes the net.Listener of the server.
func (*ServerListener) Handler ¶ added in v2.3.0
func (s *ServerListener) Handler(ctx context.Context, conn net.Conn)
Handler takes enriched context and connection from the network. It calls handler callbacks and listens the connection for messages.
func (*ServerListener) Listen ¶ added in v2.3.0
func (s *ServerListener) Listen() error
Listen starts to listen the net.Listener for a new connections from the network.
Source Files ¶
Directories ¶
Path | Synopsis |
---|---|
Package client is a JSON-RPC client.
|
Package client is a JSON-RPC client. |
Package pjson implements json marshal, unmarshal methods and encoder, decoder.
|
Package pjson implements json marshal, unmarshal methods and encoder, decoder. |
Package storage contains storage of the internal handlers.
|
Package storage contains storage of the internal handlers. |