mirror of
https://github.com/zrepl/zrepl.git
synced 2024-11-29 03:45:27 +01:00
112 lines
2.5 KiB
Go
112 lines
2.5 KiB
Go
|
package rpc
|
||
|
|
||
|
import (
|
||
|
"fmt"
|
||
|
"github.com/pkg/errors"
|
||
|
"io"
|
||
|
"reflect"
|
||
|
)
|
||
|
|
||
|
type RPCServer interface {
|
||
|
Serve() (err error)
|
||
|
RegisterEndpoint(name string, handler interface{}) (err error)
|
||
|
}
|
||
|
|
||
|
type RPCClient interface {
|
||
|
Call(endpoint string, in, out interface{}) (err error)
|
||
|
Close() (err error)
|
||
|
}
|
||
|
|
||
|
type Logger interface {
|
||
|
Printf(format string, args ...interface{})
|
||
|
}
|
||
|
|
||
|
type noLogger struct{}
|
||
|
|
||
|
func (l noLogger) Printf(format string, args ...interface{}) {}
|
||
|
func typeIsIOReader(t reflect.Type) bool {
|
||
|
return t == reflect.TypeOf((*io.Reader)(nil)).Elem()
|
||
|
}
|
||
|
|
||
|
func typeIsIOReaderPtr(t reflect.Type) bool {
|
||
|
return t == reflect.TypeOf((*io.Reader)(nil))
|
||
|
}
|
||
|
|
||
|
// An error returned by the Client if the response indicated a status code other than StatusOK
|
||
|
type RPCError struct {
|
||
|
ResponseHeader *Header
|
||
|
}
|
||
|
|
||
|
func (e *RPCError) Error() string {
|
||
|
return fmt.Sprintf("%s: %s", e.ResponseHeader.Error, e.ResponseHeader.ErrorMessage)
|
||
|
}
|
||
|
|
||
|
type RPCProtoError struct {
|
||
|
Message string
|
||
|
UnderlyingError error
|
||
|
}
|
||
|
|
||
|
func (e *RPCProtoError) Error() string {
|
||
|
return e.Message
|
||
|
}
|
||
|
|
||
|
func checkRPCParamTypes(in, out reflect.Type) (err error) {
|
||
|
if !(in.Kind() == reflect.Ptr || typeIsIOReader(in)) {
|
||
|
err = errors.Errorf("input parameter must be a pointer or an io.Reader, is of kind %s, type %s", in.Kind(), in)
|
||
|
return
|
||
|
}
|
||
|
if !(out.Kind() == reflect.Ptr) {
|
||
|
err = errors.Errorf("second input parameter (the non-error output parameter) must be a pointer or an *io.Reader")
|
||
|
return
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func checkRPCReturnType(rt reflect.Type) (err error) {
|
||
|
errInterfaceType := reflect.TypeOf((*error)(nil)).Elem()
|
||
|
if !rt.Implements(errInterfaceType) {
|
||
|
err = errors.Errorf("handler must return an error")
|
||
|
return
|
||
|
}
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
func makeEndpointDescr(handler interface{}) (descr endpointDescr, err error) {
|
||
|
|
||
|
ht := reflect.TypeOf(handler)
|
||
|
|
||
|
if ht.Kind() != reflect.Func {
|
||
|
err = errors.Errorf("handler must be of kind reflect.Func")
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if ht.NumIn() != 2 || ht.NumOut() != 1 {
|
||
|
err = errors.Errorf("handler must have exactly two input parameters and one output parameter")
|
||
|
return
|
||
|
}
|
||
|
if err = checkRPCParamTypes(ht.In(0), ht.In(1)); err != nil {
|
||
|
return
|
||
|
}
|
||
|
if err = checkRPCReturnType(ht.Out(0)); err != nil {
|
||
|
return
|
||
|
}
|
||
|
|
||
|
descr.handler = reflect.ValueOf(handler)
|
||
|
descr.inType.local = ht.In(0)
|
||
|
descr.outType.local = ht.In(1)
|
||
|
|
||
|
if typeIsIOReader(ht.In(0)) {
|
||
|
descr.inType.proto = DataTypeOctets
|
||
|
} else {
|
||
|
descr.inType.proto = DataTypeMarshaledJSON
|
||
|
}
|
||
|
|
||
|
if typeIsIOReaderPtr(ht.In(1)) {
|
||
|
descr.outType.proto = DataTypeOctets
|
||
|
} else {
|
||
|
descr.outType.proto = DataTypeMarshaledJSON
|
||
|
}
|
||
|
|
||
|
return
|
||
|
}
|