2018-03-19 17:14:50 +00:00
|
|
|
// Package mrpc contains types and functionality to facilitate creating RPC
|
2018-04-14 10:45:11 +00:00
|
|
|
// interfaces and for making calls against those same interfaces.
|
2018-03-19 17:14:50 +00:00
|
|
|
//
|
|
|
|
// This package contains a few fundamental types: Handler, Call, and
|
|
|
|
// Client. Together these form the components needed to implement nearly any RPC
|
|
|
|
// system.
|
2018-04-27 08:03:39 +00:00
|
|
|
//
|
|
|
|
// TODO document examples
|
|
|
|
// TODO document Debug?
|
2018-03-19 17:14:50 +00:00
|
|
|
package mrpc
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"fmt"
|
|
|
|
"reflect"
|
|
|
|
)
|
|
|
|
|
2018-05-10 03:29:32 +00:00
|
|
|
// Request TODO
|
|
|
|
type Request struct {
|
|
|
|
Context context.Context
|
|
|
|
|
|
|
|
// The name of the RPC method being called.
|
|
|
|
Method string
|
|
|
|
|
|
|
|
// Unmarshal takes in a pointer and unmarshals the RPC request's arguments
|
|
|
|
// into it. The properties of the unmarshaling are dependent on the
|
|
|
|
// underlying implementation of the protocol.
|
|
|
|
//
|
|
|
|
// This should only be called within ServeRPC.
|
|
|
|
Unmarshal func(interface{}) error
|
|
|
|
}
|
|
|
|
|
|
|
|
// ResponseWriter TODO
|
|
|
|
type ResponseWriter struct {
|
|
|
|
Context context.Context
|
|
|
|
|
|
|
|
Respond func(interface{})
|
|
|
|
Err func(error)
|
|
|
|
}
|
|
|
|
|
|
|
|
// Reponse TODO
|
|
|
|
type Response struct {
|
|
|
|
Context context.Context
|
|
|
|
Unmarshal func(interface{}) error
|
|
|
|
Err error
|
|
|
|
}
|
|
|
|
|
|
|
|
// Handler is a type which serves RPC calls. For each incoming Requests the
|
|
|
|
// ServeRPC method is called with a ResponseWriter which will write the call's
|
|
|
|
// response back to the client.
|
2018-03-19 17:14:50 +00:00
|
|
|
type Handler interface {
|
2018-05-10 03:29:32 +00:00
|
|
|
ServeRPC(Request, *ResponseWriter)
|
2018-03-19 17:14:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// HandlerFunc can be used to wrap an individual function which fits the
|
|
|
|
// ServeRPC signature, and use that function as a Handler
|
2018-05-10 03:29:32 +00:00
|
|
|
type HandlerFunc func(Request, *ResponseWriter)
|
2018-03-19 17:14:50 +00:00
|
|
|
|
|
|
|
// ServeRPC implements the method for the Handler interface by calling the
|
|
|
|
// underlying function
|
2018-05-10 03:29:32 +00:00
|
|
|
func (hf HandlerFunc) ServeRPC(r Request, rw *ResponseWriter) {
|
|
|
|
hf(r, rw)
|
2018-03-19 17:14:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Client is an entity which can perform RPC calls against a remote endpoint.
|
|
|
|
//
|
|
|
|
// res should be a pointer into which the result of the RPC call will be
|
|
|
|
// unmarshaled according to Client's implementation. args will be marshaled and
|
|
|
|
// sent to the remote endpoint according to Client's implementation.
|
|
|
|
type Client interface {
|
2018-05-10 03:29:32 +00:00
|
|
|
CallRPC(ctx context.Context, method string, args interface{}) Response
|
2018-03-19 17:14:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ClientFunc can be used to wrap an individual function which fits the CallRPC
|
|
|
|
// signature, and use that function as a Client
|
2018-05-10 03:29:32 +00:00
|
|
|
type ClientFunc func(context.Context, string, interface{}) Response
|
2018-03-19 17:14:50 +00:00
|
|
|
|
|
|
|
// CallRPC implements the method for the Client interface by calling the
|
|
|
|
// underlying function
|
|
|
|
func (cf ClientFunc) CallRPC(
|
|
|
|
ctx context.Context,
|
|
|
|
method string,
|
|
|
|
args interface{},
|
2018-05-10 03:29:32 +00:00
|
|
|
) Response {
|
|
|
|
return cf(ctx, method, args)
|
2018-03-19 17:14:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// ReflectClient returns a Client whose CallRPC method will use reflection to
|
|
|
|
// call the given Handler's ServeRPC method directly, using reflect.Value's Set
|
2018-05-10 03:29:32 +00:00
|
|
|
// method to copy CallRPC's args parameter into the Request's Unmarshal method's
|
|
|
|
// receiver parameter, and similarly to copy the result from ServeRPC into
|
|
|
|
// the Response's Unmarshal method's receiver parameter.
|
2018-03-19 17:14:50 +00:00
|
|
|
func ReflectClient(h Handler) Client {
|
|
|
|
into := func(dst, src interface{}) error {
|
|
|
|
dstV, srcV := reflect.ValueOf(dst), reflect.ValueOf(src)
|
|
|
|
dstVi, srcVi := reflect.Indirect(dstV), reflect.Indirect(srcV)
|
|
|
|
if !dstVi.CanSet() || dstVi.Type() != srcVi.Type() {
|
|
|
|
return fmt.Errorf("can't set value of type %v into type %v", srcV.Type(), dstV.Type())
|
|
|
|
}
|
|
|
|
dstVi.Set(srcVi)
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
return ClientFunc(func(
|
|
|
|
ctx context.Context,
|
|
|
|
method string,
|
|
|
|
args interface{},
|
2018-05-10 03:29:32 +00:00
|
|
|
) Response {
|
|
|
|
req := Request{
|
|
|
|
Context: ctx,
|
|
|
|
Method: method,
|
|
|
|
Unmarshal: func(i interface{}) error { return into(i, args) },
|
2018-03-19 17:14:50 +00:00
|
|
|
}
|
2018-05-10 03:29:32 +00:00
|
|
|
var res interface{}
|
|
|
|
var resErr error
|
|
|
|
rw := ResponseWriter{
|
|
|
|
Context: context.Background(),
|
|
|
|
Respond: func(i interface{}) { res = i },
|
|
|
|
Err: func(err error) { resErr = err },
|
2018-03-19 17:14:50 +00:00
|
|
|
}
|
|
|
|
|
2018-05-10 03:29:32 +00:00
|
|
|
h.ServeRPC(req, &rw)
|
|
|
|
|
|
|
|
return Response{
|
|
|
|
Context: rw.Context,
|
|
|
|
Unmarshal: func(i interface{}) error {
|
|
|
|
if resErr != nil {
|
|
|
|
return resErr
|
|
|
|
}
|
|
|
|
return into(i, res)
|
|
|
|
},
|
|
|
|
Err: resErr,
|
|
|
|
}
|
2018-03-19 17:14:50 +00:00
|
|
|
})
|
|
|
|
}
|