Improved middlewares
This commit is contained in:
parent
281eda83c9
commit
f304a07a8c
13 changed files with 260 additions and 74 deletions
|
@ -78,6 +78,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
"go.neonxp.dev/jsonrpc2/rpc"
|
"go.neonxp.dev/jsonrpc2/rpc"
|
||||||
|
"go.neonxp.dev/jsonrpc2/rpc/middleware"
|
||||||
"go.neonxp.dev/jsonrpc2/transport"
|
"go.neonxp.dev/jsonrpc2/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -90,7 +91,7 @@ func main() {
|
||||||
// Set options after constructor
|
// Set options after constructor
|
||||||
s.Use(
|
s.Use(
|
||||||
rpc.WithTransport(&transport.TCP{Bind: ":3000"}), // TCP transport
|
rpc.WithTransport(&transport.TCP{Bind: ":3000"}), // TCP transport
|
||||||
rpc.WithMiddleware(rpc.LoggerMiddleware(rpc.StdLogger)), // Logger middleware
|
rpc.WithMiddleware(middleware.Logger(rpc.StdLogger)), // Logger middleware
|
||||||
)
|
)
|
||||||
|
|
||||||
s.Register("multiply", rpc.H(Multiply))
|
s.Register("multiply", rpc.H(Multiply))
|
||||||
|
|
|
@ -7,7 +7,9 @@ import (
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
|
||||||
|
"github.com/qri-io/jsonschema"
|
||||||
"go.neonxp.dev/jsonrpc2/rpc"
|
"go.neonxp.dev/jsonrpc2/rpc"
|
||||||
|
"go.neonxp.dev/jsonrpc2/rpc/middleware"
|
||||||
"go.neonxp.dev/jsonrpc2/transport"
|
"go.neonxp.dev/jsonrpc2/transport"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -17,9 +19,42 @@ func main() {
|
||||||
rpc.WithTransport(&transport.HTTP{Bind: ":8000", CORSOrigin: "*"}),
|
rpc.WithTransport(&transport.HTTP{Bind: ":8000", CORSOrigin: "*"}),
|
||||||
)
|
)
|
||||||
// Set options after constructor
|
// Set options after constructor
|
||||||
|
validation, err := middleware.Validation(map[string]middleware.MethodSchema{
|
||||||
|
"divide": {
|
||||||
|
Request: *jsonschema.Must(`{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"a": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"b": {
|
||||||
|
"type": "integer",
|
||||||
|
"not":{"const":0}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["a", "b"]
|
||||||
|
}`),
|
||||||
|
Response: *jsonschema.Must(`{
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"quo": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"rem": {
|
||||||
|
"type": "integer"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["quo", "rem"]
|
||||||
|
}`),
|
||||||
|
},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
s.Use(
|
s.Use(
|
||||||
rpc.WithTransport(&transport.TCP{Bind: ":3000"}),
|
rpc.WithTransport(&transport.TCP{Bind: ":3000"}),
|
||||||
rpc.WithMiddleware(rpc.LoggerMiddleware(rpc.StdLogger)),
|
rpc.WithMiddleware(middleware.Logger(rpc.StdLogger)),
|
||||||
|
rpc.WithMiddleware(validation),
|
||||||
)
|
)
|
||||||
|
|
||||||
s.Register("multiply", rpc.H(Multiply))
|
s.Register("multiply", rpc.H(Multiply))
|
||||||
|
|
|
@ -19,7 +19,7 @@ Content-Type: application/json
|
||||||
"method": "divide",
|
"method": "divide",
|
||||||
"params": {
|
"params": {
|
||||||
"a": 10,
|
"a": 10,
|
||||||
"b": 3
|
"b": 10
|
||||||
},
|
},
|
||||||
"id": 2
|
"id": 2
|
||||||
}
|
}
|
||||||
|
@ -44,3 +44,18 @@ Content-Type: application/json
|
||||||
{"foo": "boo"}
|
{"foo": "boo"}
|
||||||
{"jsonrpc": "2.0", "method": "foo.get", "params": {"name": "myself"}, "id": "5"}
|
{"jsonrpc": "2.0", "method": "foo.get", "params": {"name": "myself"}, "id": "5"}
|
||||||
{"jsonrpc": "2.0", "method": "get_data", "id": "9"}
|
{"jsonrpc": "2.0", "method": "get_data", "id": "9"}
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "divide",
|
||||||
|
"params": {
|
||||||
|
"a": 10,
|
||||||
|
"b": 0
|
||||||
|
},
|
||||||
|
"id": "divide"
|
||||||
|
}
|
||||||
|
{
|
||||||
|
"jsonrpc": "2.0",
|
||||||
|
"method": "divide",
|
||||||
|
"params": {},
|
||||||
|
"id": "divide"
|
||||||
|
}
|
||||||
|
|
7
go.mod
7
go.mod
|
@ -2,4 +2,9 @@ module go.neonxp.dev/jsonrpc2
|
||||||
|
|
||||||
go 1.18
|
go 1.18
|
||||||
|
|
||||||
require golang.org/x/sync v0.0.0-20220513210516-0976fa681c29
|
require (
|
||||||
|
github.com/qri-io/jsonschema v0.2.1
|
||||||
|
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29
|
||||||
|
)
|
||||||
|
|
||||||
|
require github.com/qri-io/jsonpointer v0.1.1 // indirect
|
||||||
|
|
9
go.sum
9
go.sum
|
@ -1,2 +1,11 @@
|
||||||
|
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||||
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
|
github.com/qri-io/jsonpointer v0.1.1 h1:prVZBZLL6TW5vsSB9fFHFAMBLI4b0ri5vribQlTJiBA=
|
||||||
|
github.com/qri-io/jsonpointer v0.1.1/go.mod h1:DnJPaYgiKu56EuDp8TU5wFLdZIcAnb/uH9v37ZaMV64=
|
||||||
|
github.com/qri-io/jsonschema v0.2.1 h1:NNFoKms+kut6ABPf6xiKNM5214jzxAhDBrPHCJ97Wg0=
|
||||||
|
github.com/qri-io/jsonschema v0.2.1/go.mod h1:g7DPkiOsK1xv6T/Ao5scXRkd+yTFygcANPBaaqW+VrI=
|
||||||
|
github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
|
||||||
|
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||||
|
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4=
|
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29 h1:w8s32wxx3sY+OjLlv9qltkLU5yvJzxjjgiHWLjdIcw4=
|
||||||
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
golang.org/x/sync v0.0.0-20220513210516-0976fa681c29/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||||
|
|
46
rpc/contract.go
Normal file
46
rpc/contract.go
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
//Package rpc provides abstract rpc server
|
||||||
|
//
|
||||||
|
//Copyright (C) 2022 Alexander Kiryukhin <i@neonxp.dev>
|
||||||
|
//
|
||||||
|
//This file is part of go.neonxp.dev/jsonrpc2 project.
|
||||||
|
//
|
||||||
|
//This program is free software: you can redistribute it and/or modify
|
||||||
|
//it under the terms of the GNU General Public License as published by
|
||||||
|
//the Free Software Foundation, either version 3 of the License, or
|
||||||
|
//(at your option) any later version.
|
||||||
|
//
|
||||||
|
//This program is distributed in the hope that it will be useful,
|
||||||
|
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
//GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
//You should have received a copy of the GNU General Public License
|
||||||
|
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package rpc
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
)
|
||||||
|
|
||||||
|
type RpcHandler func(ctx context.Context, req *RpcRequest) *RpcResponse
|
||||||
|
|
||||||
|
type RpcRequest struct {
|
||||||
|
Jsonrpc string `json:"jsonrpc"`
|
||||||
|
Method string `json:"method"`
|
||||||
|
Params json.RawMessage `json:"params"`
|
||||||
|
Id any `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type RpcResponse struct {
|
||||||
|
Jsonrpc string `json:"jsonrpc"`
|
||||||
|
Result json.RawMessage `json:"result,omitempty"`
|
||||||
|
Error error `json:"error,omitempty"`
|
||||||
|
Id any `json:"id,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Flusher interface {
|
||||||
|
// Flush sends any buffered data to the client.
|
||||||
|
Flush()
|
||||||
|
}
|
|
@ -31,12 +31,12 @@ const (
|
||||||
)
|
)
|
||||||
|
|
||||||
var errorMap = map[int]string{
|
var errorMap = map[int]string{
|
||||||
-32700: "Parse error", // Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.
|
ErrCodeParseError: "Parse error", // Invalid JSON was received by the server. An error occurred on the server while parsing the JSON text.
|
||||||
-32600: "Invalid Request", // The JSON sent is not a valid Request object.
|
ErrCodeInvalidRequest: "Invalid Request", // The JSON sent is not a valid Request object.
|
||||||
-32601: "Method not found", // The method does not exist / is not available.
|
ErrCodeMethodNotFound: "Method not found", // The method does not exist / is not available.
|
||||||
-32602: "Invalid params", // Invalid method parameter(s).
|
ErrCodeInvalidParams: "Invalid params", // Invalid method parameter(s).
|
||||||
-32603: "Internal error", // Internal JSON-RPC error.
|
ErrCodeInternalError: "Internal error", // Internal JSON-RPC error.
|
||||||
-32000: "Other error",
|
ErrUser: "Other error",
|
||||||
}
|
}
|
||||||
|
|
||||||
//-32000 to -32099 RpcServer error Reserved for implementation-defined server-errors.
|
//-32000 to -32099 RpcServer error Reserved for implementation-defined server-errors.
|
||||||
|
|
|
@ -19,25 +19,4 @@
|
||||||
|
|
||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
type Middleware func(handler RpcHandler) RpcHandler
|
type Middleware func(handler RpcHandler) RpcHandler
|
||||||
|
|
||||||
type RpcHandler func(ctx context.Context, req *RpcRequest) *RpcResponse
|
|
||||||
|
|
||||||
func LoggerMiddleware(logger Logger) Middleware {
|
|
||||||
return func(handler RpcHandler) RpcHandler {
|
|
||||||
return func(ctx context.Context, req *RpcRequest) *RpcResponse {
|
|
||||||
t1 := time.Now().UnixMicro()
|
|
||||||
resp := handler(ctx, req)
|
|
||||||
t2 := time.Now().UnixMicro()
|
|
||||||
args := strings.ReplaceAll(string(req.Params), "\n", "")
|
|
||||||
logger.Logf("rpc call=%s, args=%s, take=%dμs", req.Method, args, (t2 - t1))
|
|
||||||
return resp
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
41
rpc/middleware/logger.go
Normal file
41
rpc/middleware/logger.go
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
//Package middleware provides middlewares for rpc server
|
||||||
|
//
|
||||||
|
//Copyright (C) 2022 Alexander Kiryukhin <i@neonxp.dev>
|
||||||
|
//
|
||||||
|
//This file is part of go.neonxp.dev/jsonrpc2 project.
|
||||||
|
//
|
||||||
|
//This program is free software: you can redistribute it and/or modify
|
||||||
|
//it under the terms of the GNU General Public License as published by
|
||||||
|
//the Free Software Foundation, either version 3 of the License, or
|
||||||
|
//(at your option) any later version.
|
||||||
|
//
|
||||||
|
//This program is distributed in the hope that it will be useful,
|
||||||
|
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
//GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
//You should have received a copy of the GNU General Public License
|
||||||
|
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"go.neonxp.dev/jsonrpc2/rpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Logger(logger rpc.Logger) rpc.Middleware {
|
||||||
|
return func(handler rpc.RpcHandler) rpc.RpcHandler {
|
||||||
|
return func(ctx context.Context, req *rpc.RpcRequest) *rpc.RpcResponse {
|
||||||
|
t1 := time.Now().UnixMicro()
|
||||||
|
resp := handler(ctx, req)
|
||||||
|
t2 := time.Now().UnixMicro()
|
||||||
|
args := strings.ReplaceAll(string(req.Params), "\n", "")
|
||||||
|
logger.Logf("rpc call=%s, args=%s, take=%dμs", req.Method, args, (t2 - t1))
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
72
rpc/middleware/validation.go
Normal file
72
rpc/middleware/validation.go
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
//Package middleware provides middlewares for rpc server
|
||||||
|
//
|
||||||
|
//Copyright (C) 2022 Alexander Kiryukhin <i@neonxp.dev>
|
||||||
|
//
|
||||||
|
//This file is part of go.neonxp.dev/jsonrpc2 project.
|
||||||
|
//
|
||||||
|
//This program is free software: you can redistribute it and/or modify
|
||||||
|
//it under the terms of the GNU General Public License as published by
|
||||||
|
//the Free Software Foundation, either version 3 of the License, or
|
||||||
|
//(at your option) any later version.
|
||||||
|
//
|
||||||
|
//This program is distributed in the hope that it will be useful,
|
||||||
|
//but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
//MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
//GNU General Public License for more details.
|
||||||
|
//
|
||||||
|
//You should have received a copy of the GNU General Public License
|
||||||
|
//along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
package middleware
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/qri-io/jsonschema"
|
||||||
|
|
||||||
|
"go.neonxp.dev/jsonrpc2/rpc"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MethodSchema struct {
|
||||||
|
Request jsonschema.Schema
|
||||||
|
Response jsonschema.Schema
|
||||||
|
}
|
||||||
|
|
||||||
|
func Validation(serviceSchema map[string]MethodSchema) (rpc.Middleware, error) {
|
||||||
|
return func(handler rpc.RpcHandler) rpc.RpcHandler {
|
||||||
|
return func(ctx context.Context, req *rpc.RpcRequest) *rpc.RpcResponse {
|
||||||
|
if rs, ok := serviceSchema[strings.ToLower(req.Method)]; ok {
|
||||||
|
if errResp := formatError(ctx, req.Id, rs.Request, req.Params); errResp != nil {
|
||||||
|
return errResp
|
||||||
|
}
|
||||||
|
resp := handler(ctx, req)
|
||||||
|
if errResp := formatError(ctx, req.Id, rs.Response, resp.Result); errResp != nil {
|
||||||
|
return errResp
|
||||||
|
}
|
||||||
|
return resp
|
||||||
|
}
|
||||||
|
return handler(ctx, req)
|
||||||
|
}
|
||||||
|
}, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func formatError(ctx context.Context, requestId any, schema jsonschema.Schema, data json.RawMessage) *rpc.RpcResponse {
|
||||||
|
errs, err := schema.ValidateBytes(ctx, data)
|
||||||
|
if err != nil {
|
||||||
|
return rpc.ErrorResponse(requestId, err)
|
||||||
|
}
|
||||||
|
if errs != nil && len(errs) > 0 {
|
||||||
|
messages := []string{}
|
||||||
|
for _, msg := range errs {
|
||||||
|
messages = append(messages, fmt.Sprintf("%s: %s", msg.PropertyPath, msg.Message))
|
||||||
|
}
|
||||||
|
return rpc.ErrorResponse(requestId, rpc.Error{
|
||||||
|
Code: rpc.ErrCodeInvalidParams,
|
||||||
|
Message: strings.Join(messages, "\n"),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -19,7 +19,9 @@
|
||||||
|
|
||||||
package rpc
|
package rpc
|
||||||
|
|
||||||
import "go.neonxp.dev/jsonrpc2/transport"
|
import (
|
||||||
|
"go.neonxp.dev/jsonrpc2/transport"
|
||||||
|
)
|
||||||
|
|
||||||
type Option func(s *RpcServer)
|
type Option func(s *RpcServer)
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,7 @@ import (
|
||||||
"context"
|
"context"
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"io"
|
"io"
|
||||||
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
|
|
||||||
"golang.org/x/sync/errgroup"
|
"golang.org/x/sync/errgroup"
|
||||||
|
@ -34,7 +35,7 @@ const version = "2.0"
|
||||||
|
|
||||||
type RpcServer struct {
|
type RpcServer struct {
|
||||||
logger Logger
|
logger Logger
|
||||||
handlers map[string]Handler
|
handlers map[string]HandlerFunc
|
||||||
middlewares []Middleware
|
middlewares []Middleware
|
||||||
transports []transport.Transport
|
transports []transport.Transport
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
|
@ -43,7 +44,7 @@ type RpcServer struct {
|
||||||
func New(opts ...Option) *RpcServer {
|
func New(opts ...Option) *RpcServer {
|
||||||
s := &RpcServer{
|
s := &RpcServer{
|
||||||
logger: nopLogger{},
|
logger: nopLogger{},
|
||||||
handlers: map[string]Handler{},
|
handlers: map[string]HandlerFunc{},
|
||||||
transports: []transport.Transport{},
|
transports: []transport.Transport{},
|
||||||
mu: sync.RWMutex{},
|
mu: sync.RWMutex{},
|
||||||
}
|
}
|
||||||
|
@ -59,10 +60,11 @@ func (r *RpcServer) Use(opts ...Option) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RpcServer) Register(method string, handler Handler) {
|
func (r *RpcServer) Register(method string, handler HandlerFunc) {
|
||||||
r.mu.Lock()
|
r.mu.Lock()
|
||||||
defer r.mu.Unlock()
|
defer r.mu.Unlock()
|
||||||
r.handlers[method] = handler
|
r.logger.Logf("Register method %s", method)
|
||||||
|
r.handlers[strings.ToLower(method)] = handler
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *RpcServer) Run(ctx context.Context) error {
|
func (r *RpcServer) Run(ctx context.Context) error {
|
||||||
|
@ -99,7 +101,7 @@ func (r *RpcServer) Resolve(ctx context.Context, rd io.Reader, w io.Writer, para
|
||||||
defer mu.Unlock()
|
defer mu.Unlock()
|
||||||
if err := enc.Encode(resp); err != nil {
|
if err := enc.Encode(resp); err != nil {
|
||||||
r.logger.Logf("Can't write response: %v", err)
|
r.logger.Logf("Can't write response: %v", err)
|
||||||
WriteError(ErrCodeInternalError, enc)
|
enc.Encode(ErrorResponse(req.Id, ErrorFromCode(ErrCodeInternalError)))
|
||||||
}
|
}
|
||||||
if w, canFlush := w.(Flusher); canFlush {
|
if w, canFlush := w.(Flusher); canFlush {
|
||||||
w.Flush()
|
w.Flush()
|
||||||
|
@ -122,53 +124,32 @@ func (r *RpcServer) Resolve(ctx context.Context, rd io.Reader, w io.Writer, para
|
||||||
|
|
||||||
func (r *RpcServer) callMethod(ctx context.Context, req *RpcRequest) *RpcResponse {
|
func (r *RpcServer) callMethod(ctx context.Context, req *RpcRequest) *RpcResponse {
|
||||||
r.mu.RLock()
|
r.mu.RLock()
|
||||||
h, ok := r.handlers[req.Method]
|
h, ok := r.handlers[strings.ToLower(req.Method)]
|
||||||
r.mu.RUnlock()
|
r.mu.RUnlock()
|
||||||
if !ok {
|
if !ok {
|
||||||
return &RpcResponse{
|
return ErrorResponse(req.Id, ErrorFromCode(ErrCodeMethodNotFound))
|
||||||
Jsonrpc: version,
|
|
||||||
Error: ErrorFromCode(ErrCodeMethodNotFound),
|
|
||||||
Id: req.Id,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
resp, err := h(ctx, req.Params)
|
resp, err := h(ctx, req.Params)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.logger.Logf("User error %v", err)
|
r.logger.Logf("User error %v", err)
|
||||||
return &RpcResponse{
|
return ErrorResponse(req.Id, err)
|
||||||
Jsonrpc: version,
|
|
||||||
Error: err,
|
|
||||||
Id: req.Id,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return ResultResponse(req.Id, resp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ResultResponse(id any, resp json.RawMessage) *RpcResponse {
|
||||||
return &RpcResponse{
|
return &RpcResponse{
|
||||||
Jsonrpc: version,
|
Jsonrpc: version,
|
||||||
Result: resp,
|
Result: resp,
|
||||||
Id: req.Id,
|
Id: id,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func WriteError(code int, enc *json.Encoder) {
|
func ErrorResponse(id any, err error) *RpcResponse {
|
||||||
enc.Encode(RpcResponse{
|
return &RpcResponse{
|
||||||
Jsonrpc: version,
|
Jsonrpc: version,
|
||||||
Error: ErrorFromCode(code),
|
Error: err,
|
||||||
})
|
Id: id,
|
||||||
}
|
}
|
||||||
|
|
||||||
type RpcRequest struct {
|
|
||||||
Jsonrpc string `json:"jsonrpc"`
|
|
||||||
Method string `json:"method"`
|
|
||||||
Params json.RawMessage `json:"params"`
|
|
||||||
Id any `json:"id"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type RpcResponse struct {
|
|
||||||
Jsonrpc string `json:"jsonrpc"`
|
|
||||||
Result json.RawMessage `json:"result,omitempty"`
|
|
||||||
Error error `json:"error,omitempty"`
|
|
||||||
Id any `json:"id,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type Flusher interface {
|
|
||||||
// Flush sends any buffered data to the client.
|
|
||||||
Flush()
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -24,7 +24,7 @@ import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
func H[RQ any, RS any](handler func(context.Context, *RQ) (RS, error)) Handler {
|
func H[RQ any, RS any](handler func(context.Context, *RQ) (RS, error)) HandlerFunc {
|
||||||
return func(ctx context.Context, in json.RawMessage) (json.RawMessage, error) {
|
return func(ctx context.Context, in json.RawMessage) (json.RawMessage, error) {
|
||||||
req := new(RQ)
|
req := new(RQ)
|
||||||
if err := json.Unmarshal(in, req); err != nil {
|
if err := json.Unmarshal(in, req); err != nil {
|
||||||
|
@ -41,4 +41,4 @@ func H[RQ any, RS any](handler func(context.Context, *RQ) (RS, error)) Handler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type Handler func(context.Context, json.RawMessage) (json.RawMessage, error)
|
type HandlerFunc func(context.Context, json.RawMessage) (json.RawMessage, error)
|
||||||
|
|
Loading…
Reference in a new issue