good morning!!!!

Skip to content
Snippets Groups Projects
protocol.go 3.26 KiB
Newer Older
a's avatar
rpc
a committed
package jrpc

import (
	"context"
a's avatar
a committed
	"errors"
a's avatar
a committed
	"net/http"
a's avatar
ok  
a committed

	json "github.com/goccy/go-json"
a's avatar
ok  
a committed
	jsoniter "github.com/json-iterator/go"
a's avatar
rpc
a committed
)

type HandlerFunc func(w ResponseWriter, r *Request)

type Handler interface {
	ServeRPC(w ResponseWriter, r *Request)
}

a's avatar
a committed
type ResponseWriter interface {
	Send(v any, err error) error
	Option(k string, v any)
a's avatar
a committed
	Header() http.Header
a's avatar
a committed

	Notify(v any) error
a's avatar
a committed
}

func (fn HandlerFunc) ServeRPC(w ResponseWriter, r *Request) {
	(fn)(w, r)
}

a's avatar
rpc
a committed
type Request struct {
	ctx context.Context
	msg jsonrpcMessage
a's avatar
rpc
a committed
}

a's avatar
a committed
func NewRequest(ctx context.Context, id string, method string, params any) *Request {
	r := &Request{ctx: ctx}
	pms, _ := json.Marshal(params)
a's avatar
a committed
	r.msg = jsonrpcMessage{
a's avatar
a committed
		ID:     NewStringIDPtr(id),
		Method: method,
		Params: pms,
a's avatar
a committed
	}
	return r
}

a's avatar
rpc
a committed
func (r *Request) Method() string {
	return r.msg.Method
}
a's avatar
rpc
a committed
func (r *Request) Params() json.RawMessage {
	return r.msg.Params
}
func (r *Request) ParamSlice() []any {
	var params []any
	json.Unmarshal(r.msg.Params, &params)
	return params
}

a's avatar
ok  
a committed
var jpool = jsoniter.NewIterator(jsoniter.ConfigCompatibleWithStandardLibrary).Pool()

func (r *Request) Iter(fn func(j *jsoniter.Iterator) error) error {
	it := jpool.BorrowIterator(r.Params())
	defer jpool.ReturnIterator(it)
	return fn(it)
}

a's avatar
a committed
func (r *Request) ParamArray(a ...any) error {
	var params []json.RawMessage
	json.Unmarshal(r.msg.Params, &params)
a's avatar
a committed
	for idx, v := range params {
		if len(v) > idx {
			err := json.Unmarshal(v, &a[idx])
a's avatar
a committed
			if err != nil {
				return err
			}
		} else {
			break
		}
	}
	return nil
}

func (r *Request) ParamInto(v any) error {
	return json.Unmarshal(r.msg.Params, &v)
a's avatar
rpc
a committed
func (r *Request) Context() context.Context {
	return r.ctx
}

func (r *Request) Remote() string {
	return r.peer.RemoteAddr
}

func (r *Request) Peer() PeerInfo {
	return r.peer
}

a's avatar
rpc
a committed
func (r *Request) WithContext(ctx context.Context) *Request {
	if ctx == nil {
		panic("nil context")
	}
	r.ctx = ctx
	r2 := new(Request)
	*r2 = *r
	r2.ctx = ctx
	r2.msg = r.msg
	return r2
}
a's avatar
rpc
a committed
func (r *Request) Msg() jsonrpcMessage {
	return r.msg
}

type ResponseWriterMsg struct {
a's avatar
a committed
	r *Request
	n *Notifier
	s *Subscription

	msg *jsonrpcMessage
a's avatar
a committed

	options options
}

type options struct {
a's avatar
rpc
a committed
}

a's avatar
a committed
func UpgradeToSubscription(w ResponseWriter, r *Request) (*Subscription, error) {
	not, ok := NotifierFromContext(r.ctx)
	if !ok || not == nil {
		return nil, errors.New("subscription not supported")
	}
	return not.CreateSubscription(), nil
}

a's avatar
rpc
a committed
func NewReaderResponseWriterMsg(r *Request) *ResponseWriterMsg {
a's avatar
a committed
	rw := &ResponseWriterMsg{
a's avatar
rpc
a committed
		r: r,
	}
a's avatar
a committed
	rw.n, _ = NotifierFromContext(r.ctx)
a's avatar
a committed
	return rw
a's avatar
rpc
a committed
}

a's avatar
a committed
func (w *ResponseWriterMsg) Header() http.Header {
a's avatar
a committed
	wh := w.r.Peer().HTTP.WriteHeaders
	return wh
a's avatar
a committed
}

func (w *ResponseWriterMsg) Option(k string, v any) {
}

a's avatar
rpc
a committed
func (w *ResponseWriterMsg) Send(args any, e error) (err error) {
	cm := w.r.Msg()
	if e != nil {
		w.msg = cm.errorResponse(e)
		return nil
	}
a's avatar
a committed
	switch c := args.(type) {
	case *Subscription:
		w.s = c
	default:
a's avatar
a committed
	}
a's avatar
a committed
	w.msg = cm.response(args)
a's avatar
a committed
	return nil
}

func (w *ResponseWriterMsg) Notify(args any) (err error) {
a's avatar
a committed
	if w.s == nil || w.n == nil {
		return ErrSubscriptionNotFound
a's avatar
a committed
	}
	bts, _ := json.Marshal(args)
a's avatar
a committed
	err = w.n.send(w.s, bts)
	if err != nil {
		return err
a's avatar
a committed
	}
a's avatar
rpc
a committed
	return nil
}
a's avatar
a committed

func (w *ResponseWriterMsg) Result() *jsonrpcMessage {
	return w.msg
}