2022-06-21 13:29:57 +02:00
|
|
|
package rpcmiddleware
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
"fmt"
|
|
|
|
|
"reflect"
|
|
|
|
|
|
|
|
|
|
"github.com/lightningnetwork/lnd/lnrpc"
|
|
|
|
|
"google.golang.org/protobuf/proto"
|
|
|
|
|
"google.golang.org/protobuf/reflect/protoreflect"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var (
|
2022-06-28 14:46:51 +02:00
|
|
|
// ErrNotSupported is returned if a specific method is called that is
|
|
|
|
|
// not supported by the RPC middleware interceptor checker.
|
|
|
|
|
ErrNotSupported = fmt.Errorf("method not supported")
|
|
|
|
|
|
2022-06-21 13:29:57 +02:00
|
|
|
// errorType is the reflection type of the error interface.
|
|
|
|
|
errorType = reflect.TypeOf((*error)(nil)).Elem()
|
|
|
|
|
|
2022-10-15 09:03:58 +02:00
|
|
|
// ctxType is the reflection type of the context.Context interface.
|
|
|
|
|
ctxType = reflect.TypeOf((*context.Context)(nil)).Elem()
|
|
|
|
|
|
2022-06-21 13:29:57 +02:00
|
|
|
// protoMessageType is the reflection type of the proto.Message
|
|
|
|
|
// interface.
|
|
|
|
|
protoMessageType = reflect.TypeOf((*proto.Message)(nil)).Elem()
|
|
|
|
|
|
2022-06-28 14:46:51 +02:00
|
|
|
// passThroughMessageHandler is a messageHandler that does not modify
|
|
|
|
|
// the message and just passes it through.
|
2022-10-15 09:03:58 +02:00
|
|
|
passThroughMessageHandler messageHandler = func(context.Context,
|
2022-06-21 13:29:57 +02:00
|
|
|
proto.Message) (proto.Message, error) {
|
|
|
|
|
|
|
|
|
|
return nil, nil
|
|
|
|
|
}
|
2022-06-28 14:46:51 +02:00
|
|
|
|
|
|
|
|
// PassThroughErrorHandler is an ErrorHandler that does not modify an
|
|
|
|
|
// error and instead just passes it through.
|
2024-06-04 16:58:49 -04:00
|
|
|
PassThroughErrorHandler ErrorHandler = func(context.Context, error) (
|
|
|
|
|
error, error) {
|
|
|
|
|
|
2022-06-28 14:46:51 +02:00
|
|
|
return nil, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// messageDenyHandler disallows the given message.
|
2022-10-15 09:03:58 +02:00
|
|
|
messageDenyHandler messageHandler = func(context.Context,
|
|
|
|
|
proto.Message) (proto.Message, error) {
|
2022-06-28 14:46:51 +02:00
|
|
|
|
|
|
|
|
return nil, ErrNotSupported
|
|
|
|
|
}
|
2022-06-21 13:29:57 +02:00
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// RequestInterceptor is a type that can intercept an RPC request.
|
|
|
|
|
type RequestInterceptor interface {
|
|
|
|
|
// Name returns the name of the interceptor.
|
|
|
|
|
Name() string
|
|
|
|
|
|
|
|
|
|
// ReadOnly returns true if this interceptor should be registered in
|
|
|
|
|
// read-only mode. In read-only mode no custom caveat name can be
|
|
|
|
|
// specified.
|
|
|
|
|
ReadOnly() bool
|
|
|
|
|
|
|
|
|
|
// CustomCaveatName returns the name of the custom caveat that is
|
|
|
|
|
// expected to be handled by this interceptor. Cannot be specified in
|
|
|
|
|
// read-only mode.
|
|
|
|
|
CustomCaveatName() string
|
|
|
|
|
|
|
|
|
|
// Intercept processes an RPC middleware interception request and
|
|
|
|
|
// returns the interception result which either accepts or rejects the
|
|
|
|
|
// intercepted message.
|
|
|
|
|
Intercept(context.Context,
|
|
|
|
|
*lnrpc.RPCMiddlewareRequest) (*lnrpc.RPCMiddlewareResponse,
|
|
|
|
|
error)
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-28 14:46:51 +02:00
|
|
|
// messageHandler is a function type for a generic gRPC message handler that
|
|
|
|
|
// can pass through the message (=return nil, nil), replace the message with a
|
|
|
|
|
// new message of the same type (=return non-nil message, nil error) or abort
|
|
|
|
|
// the call by returning a non-nil error. If the message is a request, then
|
|
|
|
|
// returning a non-nil error will reject the request.
|
2022-10-15 09:03:58 +02:00
|
|
|
type messageHandler func(context.Context, proto.Message) (proto.Message, error)
|
2022-06-21 13:29:57 +02:00
|
|
|
|
2022-06-28 14:46:51 +02:00
|
|
|
// ErrorHandler is a function type for a generic gRPC error handler. It can
|
|
|
|
|
// pass through the error unchanged (=return nil, nil), replace the error with
|
|
|
|
|
// a different one (=return non-nil error, nil error) or abort by returning a
|
|
|
|
|
// non-nil error.
|
2024-06-04 16:58:49 -04:00
|
|
|
type ErrorHandler func(ctx context.Context, respErr error) (error, error)
|
2022-06-21 13:29:57 +02:00
|
|
|
|
|
|
|
|
// RoundTripChecker is a type that represents a basic request/response round
|
|
|
|
|
// trip checker.
|
|
|
|
|
type RoundTripChecker interface {
|
2022-06-28 14:46:51 +02:00
|
|
|
// HandlesRequest returns true if the checker accepts protobuf request
|
2022-06-21 13:29:57 +02:00
|
|
|
// messages of the given type. This is mainly a safety feature to make
|
|
|
|
|
// sure a round trip checker is implemented correctly.
|
2022-06-28 14:46:51 +02:00
|
|
|
HandlesRequest(protoreflect.MessageType) bool
|
2022-06-21 13:29:57 +02:00
|
|
|
|
|
|
|
|
// HandlesResponse returns true if the checker can handle protobuf
|
|
|
|
|
// response messages of the given type. This is mainly a safety feature
|
|
|
|
|
// to make sure a round trip checker is implemented correctly.
|
|
|
|
|
HandlesResponse(protoreflect.MessageType) bool
|
|
|
|
|
|
|
|
|
|
// HandleRequest is called for each incoming gRPC request message of the
|
2022-06-28 14:46:51 +02:00
|
|
|
// type declared to be accepted by HandlesRequest. The handler can
|
|
|
|
|
// accept the request as is (=return nil, nil), replace the request with
|
|
|
|
|
// a new message of the same type (=return non-nil message, nil) or
|
|
|
|
|
// refuse (=return non-nil error with rejection reason) an incoming
|
|
|
|
|
// request.
|
2022-10-15 09:03:58 +02:00
|
|
|
HandleRequest(context.Context, proto.Message) (proto.Message, error)
|
2022-06-21 13:29:57 +02:00
|
|
|
|
|
|
|
|
// HandleResponse is called for each outgoing gRPC response message of
|
|
|
|
|
// the type declared to be handled by HandlesResponse. The handler can
|
|
|
|
|
// pass through the response (=return nil, nil), replace the response
|
|
|
|
|
// with a new message of the same type (=return non-nil message, nil
|
|
|
|
|
// error) or abort the call by returning a non-nil error.
|
2022-10-15 09:03:58 +02:00
|
|
|
HandleResponse(context.Context, proto.Message) (proto.Message, error)
|
2022-06-28 14:46:51 +02:00
|
|
|
|
|
|
|
|
// HandleErrorResponse is called for any error response.
|
|
|
|
|
// The handler can pass through the error (=return nil, nil), replace
|
|
|
|
|
// the response error with a new one (=return non-nil error, nil) or
|
|
|
|
|
// abort by returning a non nil error (=return nil, non-nil error).
|
2024-06-04 16:58:49 -04:00
|
|
|
HandleErrorResponse(context.Context, error) (error, error)
|
2022-06-21 13:29:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DefaultChecker is the default implementation of a round trip checker.
|
|
|
|
|
type DefaultChecker struct {
|
|
|
|
|
requestType protoreflect.MessageType
|
|
|
|
|
responseType protoreflect.MessageType
|
2022-06-28 14:46:51 +02:00
|
|
|
requestHandler messageHandler
|
|
|
|
|
responseHandler messageHandler
|
|
|
|
|
errorHandler ErrorHandler
|
2022-06-21 13:29:57 +02:00
|
|
|
}
|
|
|
|
|
|
2022-06-28 14:46:51 +02:00
|
|
|
// A compile-time check to ensure that DefaultChecker implements
|
|
|
|
|
// RoundTripChecker.
|
|
|
|
|
var _ RoundTripChecker = (*DefaultChecker)(nil)
|
|
|
|
|
|
|
|
|
|
// HandlesRequest returns true if the checker accepts protobuf request messages
|
2022-06-21 13:29:57 +02:00
|
|
|
// of the given type. This is mainly a safety feature to make sure a round trip
|
|
|
|
|
// checker is implemented correctly.
|
2022-06-28 14:46:51 +02:00
|
|
|
func (r *DefaultChecker) HandlesRequest(t protoreflect.MessageType) bool {
|
2022-06-21 13:29:57 +02:00
|
|
|
return t == r.requestType
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// HandlesResponse returns true if the checker can handle protobuf response
|
|
|
|
|
// messages of the given type. This is mainly a safety feature to make sure a
|
|
|
|
|
// round trip checker is implemented correctly.
|
|
|
|
|
func (r *DefaultChecker) HandlesResponse(t protoreflect.MessageType) bool {
|
|
|
|
|
return t == r.responseType
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-15 09:03:58 +02:00
|
|
|
// HandleRequest is called for each incoming gRPC request message of the type
|
|
|
|
|
// declared to be accepted by HandlesRequest. The handler can accept the request
|
|
|
|
|
// as is (=return nil, nil), replace the request with a new message of the same
|
|
|
|
|
// type (=return non-nil message, nil) or refuse (=return non-nil error with
|
|
|
|
|
// rejection reason) an incoming request.
|
|
|
|
|
func (r *DefaultChecker) HandleRequest(ctx context.Context,
|
|
|
|
|
req proto.Message) (proto.Message, error) {
|
2022-06-28 14:46:51 +02:00
|
|
|
|
2022-10-15 09:03:58 +02:00
|
|
|
return r.requestHandler(ctx, req)
|
2022-06-21 13:29:57 +02:00
|
|
|
}
|
|
|
|
|
|
2022-10-15 09:03:58 +02:00
|
|
|
// HandleResponse is called for each outgoing gRPC response message of the type
|
|
|
|
|
// declared to be handled by HandlesResponse. The handler can pass through the
|
|
|
|
|
// response (=return nil, nil), replace the response with a new message of the
|
|
|
|
|
// same type (=return non-nil message, nil error) or abort the call by returning
|
|
|
|
|
// a non-nil error.
|
|
|
|
|
func (r *DefaultChecker) HandleResponse(ctx context.Context,
|
|
|
|
|
resp proto.Message) (proto.Message, error) {
|
2022-06-21 13:29:57 +02:00
|
|
|
|
2022-10-15 09:03:58 +02:00
|
|
|
return r.responseHandler(ctx, resp)
|
2022-06-21 13:29:57 +02:00
|
|
|
}
|
|
|
|
|
|
2022-06-28 14:46:51 +02:00
|
|
|
// HandleErrorResponse is called for any error response.
|
|
|
|
|
// The handler can pass through the error (=return nil, nil), replace
|
|
|
|
|
// the response error with a new one (=return non-nil error, nil) or
|
|
|
|
|
// abort by returning a non nil error (=return nil, non-nil error).
|
2024-06-04 16:58:49 -04:00
|
|
|
func (r *DefaultChecker) HandleErrorResponse(ctx context.Context,
|
|
|
|
|
respErr error) (error, error) {
|
|
|
|
|
|
|
|
|
|
return r.errorHandler(ctx, respErr)
|
2022-06-28 14:46:51 +02:00
|
|
|
}
|
|
|
|
|
|
2022-06-21 13:29:57 +02:00
|
|
|
// NewPassThrough returns a round trip checker that allows the incoming request
|
|
|
|
|
// and passes through the response unmodified.
|
|
|
|
|
func NewPassThrough(requestSample proto.Message,
|
|
|
|
|
responseSample proto.Message) *DefaultChecker {
|
|
|
|
|
|
|
|
|
|
return &DefaultChecker{
|
|
|
|
|
requestType: requestSample.ProtoReflect().Type(),
|
|
|
|
|
responseType: responseSample.ProtoReflect().Type(),
|
2022-06-28 14:46:51 +02:00
|
|
|
requestHandler: passThroughMessageHandler,
|
|
|
|
|
responseHandler: passThroughMessageHandler,
|
|
|
|
|
errorHandler: PassThroughErrorHandler,
|
2022-06-21 13:29:57 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewRequestChecker returns a round trip checker that inspects the incoming
|
|
|
|
|
// request and passes through the response unmodified.
|
|
|
|
|
func NewRequestChecker(requestSample proto.Message,
|
|
|
|
|
responseSample proto.Message,
|
|
|
|
|
typedRequestHandler interface{}) *DefaultChecker {
|
|
|
|
|
|
|
|
|
|
return &DefaultChecker{
|
|
|
|
|
requestType: requestSample.ProtoReflect().Type(),
|
|
|
|
|
responseType: responseSample.ProtoReflect().Type(),
|
2022-06-28 14:46:51 +02:00
|
|
|
requestHandler: newReflectionRequestCheckHandler(
|
|
|
|
|
requestSample, typedRequestHandler,
|
|
|
|
|
),
|
|
|
|
|
responseHandler: passThroughMessageHandler,
|
|
|
|
|
errorHandler: PassThroughErrorHandler,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewRequestDenier returns a round trip checker that denies the given requests.
|
|
|
|
|
func NewRequestDenier(requestSample proto.Message,
|
|
|
|
|
responseSample proto.Message) *DefaultChecker {
|
|
|
|
|
|
|
|
|
|
return &DefaultChecker{
|
|
|
|
|
requestType: requestSample.ProtoReflect().Type(),
|
|
|
|
|
responseType: responseSample.ProtoReflect().Type(),
|
|
|
|
|
requestHandler: messageDenyHandler,
|
|
|
|
|
responseHandler: messageDenyHandler,
|
|
|
|
|
errorHandler: PassThroughErrorHandler,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewRequestRewriter returns a round trip checker that inspects and potentially
|
|
|
|
|
// modifies the incoming request and passes through the response unmodified.
|
|
|
|
|
func NewRequestRewriter(requestSample proto.Message,
|
|
|
|
|
responseSample proto.Message,
|
|
|
|
|
typedRequestHandler interface{}) *DefaultChecker {
|
|
|
|
|
|
|
|
|
|
return &DefaultChecker{
|
|
|
|
|
requestType: requestSample.ProtoReflect().Type(),
|
|
|
|
|
responseType: responseSample.ProtoReflect().Type(),
|
|
|
|
|
requestHandler: newReflectionMessageHandler(
|
2022-06-21 13:29:57 +02:00
|
|
|
requestSample, typedRequestHandler,
|
|
|
|
|
),
|
2022-06-28 14:46:51 +02:00
|
|
|
responseHandler: passThroughMessageHandler,
|
|
|
|
|
errorHandler: PassThroughErrorHandler,
|
2022-06-21 13:29:57 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// NewResponseRewriter returns a round trip checker that allows the incoming
|
|
|
|
|
// request and inspects and potentially modifies the response.
|
|
|
|
|
func NewResponseRewriter(requestSample proto.Message,
|
2022-06-28 14:46:51 +02:00
|
|
|
responseSample proto.Message, typedResponseHandler interface{},
|
|
|
|
|
errorHandler ErrorHandler) *DefaultChecker {
|
2022-06-21 13:29:57 +02:00
|
|
|
|
|
|
|
|
return &DefaultChecker{
|
|
|
|
|
requestType: requestSample.ProtoReflect().Type(),
|
|
|
|
|
responseType: responseSample.ProtoReflect().Type(),
|
2022-06-28 14:46:51 +02:00
|
|
|
requestHandler: passThroughMessageHandler,
|
|
|
|
|
responseHandler: newReflectionMessageHandler(
|
2022-06-21 13:29:57 +02:00
|
|
|
responseSample, typedResponseHandler,
|
|
|
|
|
),
|
2022-06-28 14:46:51 +02:00
|
|
|
errorHandler: errorHandler,
|
2022-06-21 13:29:57 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-17 16:50:44 +01:00
|
|
|
// NewResponseEmptier returns a round trip checker that allows the incoming
|
|
|
|
|
// request and replaces the response with an empty one.
|
|
|
|
|
func NewResponseEmptier[reqT, respT proto.Message]() *DefaultChecker {
|
|
|
|
|
req := *new(reqT)
|
|
|
|
|
resp := *new(respT)
|
|
|
|
|
return &DefaultChecker{
|
|
|
|
|
requestType: req.ProtoReflect().Type(),
|
|
|
|
|
responseType: resp.ProtoReflect().Type(),
|
|
|
|
|
requestHandler: passThroughMessageHandler,
|
|
|
|
|
responseHandler: newReflectionMessageHandler(
|
|
|
|
|
resp, func(context.Context, respT) (proto.Message,
|
|
|
|
|
error) {
|
|
|
|
|
|
|
|
|
|
return *new(respT), nil
|
|
|
|
|
},
|
|
|
|
|
),
|
|
|
|
|
errorHandler: PassThroughErrorHandler,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-21 13:29:57 +02:00
|
|
|
// NewFullChecker returns a round trip checker that both inspects the incoming
|
|
|
|
|
// request and response and potentially modifies the response.
|
|
|
|
|
func NewFullChecker(requestSample proto.Message,
|
|
|
|
|
responseSample proto.Message, typedRequestHandler interface{},
|
2022-06-28 14:46:51 +02:00
|
|
|
typedResponseHandler interface{},
|
|
|
|
|
errorHandler ErrorHandler) *DefaultChecker {
|
2022-06-21 13:29:57 +02:00
|
|
|
|
|
|
|
|
return &DefaultChecker{
|
|
|
|
|
requestType: requestSample.ProtoReflect().Type(),
|
|
|
|
|
responseType: responseSample.ProtoReflect().Type(),
|
2022-06-28 14:46:51 +02:00
|
|
|
requestHandler: newReflectionRequestCheckHandler(
|
2022-06-21 13:29:57 +02:00
|
|
|
requestSample, typedRequestHandler,
|
|
|
|
|
),
|
2022-06-28 14:46:51 +02:00
|
|
|
responseHandler: newReflectionMessageHandler(
|
2022-06-21 13:29:57 +02:00
|
|
|
responseSample, typedResponseHandler,
|
|
|
|
|
),
|
2022-06-28 14:46:51 +02:00
|
|
|
errorHandler: errorHandler,
|
2022-06-21 13:29:57 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-28 14:46:51 +02:00
|
|
|
// NewFullRewriter returns a round trip checker that both inspects the incoming
|
|
|
|
|
// request and response and potentially modifies the both the request and
|
|
|
|
|
// response.
|
|
|
|
|
func NewFullRewriter(requestSample proto.Message,
|
|
|
|
|
responseSample proto.Message, typedRequestHandler interface{},
|
|
|
|
|
typedResponseHandler interface{},
|
|
|
|
|
errHandler ErrorHandler) *DefaultChecker {
|
|
|
|
|
|
|
|
|
|
return &DefaultChecker{
|
|
|
|
|
requestType: requestSample.ProtoReflect().Type(),
|
|
|
|
|
responseType: responseSample.ProtoReflect().Type(),
|
|
|
|
|
requestHandler: newReflectionMessageHandler(
|
|
|
|
|
requestSample, typedRequestHandler,
|
|
|
|
|
),
|
|
|
|
|
responseHandler: newReflectionMessageHandler(
|
|
|
|
|
responseSample, typedResponseHandler,
|
|
|
|
|
),
|
|
|
|
|
errorHandler: errHandler,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// newReflectionRequestCheckHandler returns a request handler that adapts the
|
|
|
|
|
// generic proto.Message capable request handler into one that is type specific
|
|
|
|
|
// for the given request message sample message. This requires reflection and
|
|
|
|
|
// cannot be implemented with Generics.
|
|
|
|
|
func newReflectionRequestCheckHandler(requestSample proto.Message,
|
|
|
|
|
typedHandler interface{}) messageHandler {
|
2022-06-21 13:29:57 +02:00
|
|
|
|
|
|
|
|
requestType := reflect.TypeOf(requestSample)
|
|
|
|
|
requestProtoType := requestSample.ProtoReflect().Type()
|
|
|
|
|
handlerValue := reflect.ValueOf(typedHandler)
|
|
|
|
|
|
2022-06-28 14:46:51 +02:00
|
|
|
err := validateRequestCheckHandler(handlerValue.Type(), requestType)
|
2022-06-21 13:29:57 +02:00
|
|
|
if err != nil {
|
|
|
|
|
// This is covered by unit tests and shouldn't happen in the
|
|
|
|
|
// first place, as this would be an implementation error.
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-15 09:03:58 +02:00
|
|
|
return func(ctx context.Context, req proto.Message) (proto.Message,
|
|
|
|
|
error) {
|
|
|
|
|
|
2022-06-21 13:29:57 +02:00
|
|
|
if req.ProtoReflect().Type() != requestProtoType {
|
2022-06-28 14:46:51 +02:00
|
|
|
return nil, fmt.Errorf("request handler called for "+
|
2022-06-21 13:29:57 +02:00
|
|
|
"unsupported type %v (expected %v)",
|
|
|
|
|
req.ProtoReflect().Type(), requestProtoType)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We made sure this call would succeed when creating the
|
|
|
|
|
// handler.
|
|
|
|
|
resp := handlerValue.Call([]reflect.Value{
|
2022-10-15 09:03:58 +02:00
|
|
|
reflect.ValueOf(ctx),
|
2022-06-21 13:29:57 +02:00
|
|
|
reflect.ValueOf(req),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// We also made sure the types returned from the function would
|
|
|
|
|
// be compatible with what we expect.
|
|
|
|
|
var err error
|
|
|
|
|
if resp[0].Interface() != nil {
|
|
|
|
|
err = resp[0].Interface().(error)
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-28 14:46:51 +02:00
|
|
|
return nil, err
|
2022-06-21 13:29:57 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-28 14:46:51 +02:00
|
|
|
// newReflectionMessageHandler returns a message handler that adapts the generic
|
|
|
|
|
// proto.Message capable message handler into one that is type specific for the
|
|
|
|
|
// given sample message. This requires reflection and cannot be implemented with
|
|
|
|
|
// Generics.
|
|
|
|
|
func newReflectionMessageHandler(messageSample proto.Message,
|
|
|
|
|
typedHandler interface{}) messageHandler {
|
2022-06-21 13:29:57 +02:00
|
|
|
|
2022-06-28 14:46:51 +02:00
|
|
|
messageType := reflect.TypeOf(messageSample)
|
|
|
|
|
messageProtoType := messageSample.ProtoReflect().Type()
|
2022-06-21 13:29:57 +02:00
|
|
|
handlerValue := reflect.ValueOf(typedHandler)
|
|
|
|
|
|
2022-06-28 14:46:51 +02:00
|
|
|
err := validateMessageHandler(handlerValue.Type(), messageType)
|
2022-06-21 13:29:57 +02:00
|
|
|
if err != nil {
|
|
|
|
|
// This is covered by unit tests and shouldn't happen in the
|
|
|
|
|
// first place, as this would be an implementation error.
|
|
|
|
|
panic(err)
|
|
|
|
|
}
|
|
|
|
|
|
2022-10-15 09:03:58 +02:00
|
|
|
return func(ctx context.Context, req proto.Message) (proto.Message,
|
|
|
|
|
error) {
|
|
|
|
|
|
2022-06-28 14:46:51 +02:00
|
|
|
if req.ProtoReflect().Type() != messageProtoType {
|
|
|
|
|
return nil, fmt.Errorf("message handler called for "+
|
2022-06-21 13:29:57 +02:00
|
|
|
"unsupported type %v (expected %v)",
|
2022-06-28 14:46:51 +02:00
|
|
|
req.ProtoReflect().Type(), messageProtoType)
|
2022-06-21 13:29:57 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// We made sure this call would succeed when creating the
|
|
|
|
|
// handler.
|
|
|
|
|
resp := handlerValue.Call([]reflect.Value{
|
2022-10-15 09:03:58 +02:00
|
|
|
reflect.ValueOf(ctx),
|
2022-06-21 13:29:57 +02:00
|
|
|
reflect.ValueOf(req),
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
// We also made sure the types returned from the function would
|
|
|
|
|
// be compatible with what we expect.
|
|
|
|
|
var (
|
|
|
|
|
replacementMessage proto.Message
|
|
|
|
|
err error
|
|
|
|
|
)
|
|
|
|
|
if resp[0].Interface() != nil {
|
|
|
|
|
replacementMessage = resp[0].Interface().(proto.Message)
|
|
|
|
|
}
|
|
|
|
|
if resp[1].Interface() != nil {
|
|
|
|
|
err = resp[1].Interface().(error)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return replacementMessage, err
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-28 14:46:51 +02:00
|
|
|
// validateRequestCheckHandler makes sure that the given request handler
|
|
|
|
|
// function has the correct number and types of parameters and return values.
|
|
|
|
|
func validateRequestCheckHandler(typedHandlerType reflect.Type,
|
2022-06-21 13:29:57 +02:00
|
|
|
requestType reflect.Type) error {
|
|
|
|
|
|
|
|
|
|
if typedHandlerType.Kind() != reflect.Func {
|
|
|
|
|
return fmt.Errorf("request handler must be a function")
|
|
|
|
|
}
|
2022-10-15 09:03:58 +02:00
|
|
|
if typedHandlerType.NumIn() != 2 || typedHandlerType.NumOut() != 1 {
|
|
|
|
|
return fmt.Errorf("request handler must have exactly two " +
|
2022-06-21 13:29:57 +02:00
|
|
|
"parameter and one return value")
|
|
|
|
|
}
|
2022-10-15 09:03:58 +02:00
|
|
|
if !typedHandlerType.In(0).ConvertibleTo(ctxType) {
|
|
|
|
|
return fmt.Errorf("request handler must have first parameter " +
|
|
|
|
|
"with a sub type of context.Context")
|
|
|
|
|
}
|
|
|
|
|
if !typedHandlerType.In(1).ConvertibleTo(requestType) {
|
2025-12-09 15:57:14 +00:00
|
|
|
return fmt.Errorf("request handler must have second " +
|
|
|
|
|
"parameter with a sub type of proto.Message")
|
2022-06-21 13:29:57 +02:00
|
|
|
}
|
|
|
|
|
if typedHandlerType.Out(0) != errorType {
|
|
|
|
|
return fmt.Errorf("request handler must return exactly one " +
|
|
|
|
|
"error value")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2022-06-28 14:46:51 +02:00
|
|
|
// validateMessageHandler makes sure that the given message handler function
|
2022-06-21 13:29:57 +02:00
|
|
|
// has the correct number and types of parameters and return values.
|
2022-06-28 14:46:51 +02:00
|
|
|
func validateMessageHandler(typedHandlerType reflect.Type,
|
|
|
|
|
messageType reflect.Type) error {
|
2022-06-21 13:29:57 +02:00
|
|
|
|
|
|
|
|
if typedHandlerType.Kind() != reflect.Func {
|
2022-06-28 14:46:51 +02:00
|
|
|
return fmt.Errorf("message handler must be a function")
|
2022-06-21 13:29:57 +02:00
|
|
|
}
|
2022-10-15 09:03:58 +02:00
|
|
|
if typedHandlerType.NumIn() != 2 || typedHandlerType.NumOut() != 2 {
|
|
|
|
|
return fmt.Errorf("message handler must have exactly two " +
|
2022-06-21 13:29:57 +02:00
|
|
|
"parameter and two return values")
|
|
|
|
|
}
|
2022-10-15 09:03:58 +02:00
|
|
|
if !typedHandlerType.In(0).ConvertibleTo(ctxType) {
|
|
|
|
|
return fmt.Errorf("request handler must have first parameter " +
|
|
|
|
|
"with a sub type of context.Context")
|
|
|
|
|
}
|
|
|
|
|
if !typedHandlerType.In(1).ConvertibleTo(messageType) {
|
2025-12-09 15:57:14 +00:00
|
|
|
return fmt.Errorf("message handler must have second " +
|
|
|
|
|
"parameter with a sub type of proto.Message")
|
2022-06-21 13:29:57 +02:00
|
|
|
}
|
|
|
|
|
outType0 := typedHandlerType.Out(0)
|
|
|
|
|
pmt := protoMessageType
|
|
|
|
|
if outType0 != pmt ||
|
|
|
|
|
typedHandlerType.Out(1) != errorType {
|
|
|
|
|
|
2022-06-28 14:46:51 +02:00
|
|
|
return fmt.Errorf("message handler must return exactly two " +
|
2022-06-21 13:29:57 +02:00
|
|
|
"values, proto.Message and error")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|