github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/server/grpc/subscriber.go (about)

     1  // Copyright 2020 Asim Aslam
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     https://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // Original source: github.com/micro/go-micro/v3/server/grpc/subscriber.go
    16  
    17  package grpc
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"reflect"
    23  	"runtime/debug"
    24  	"strings"
    25  
    26  	"github.com/tickoalcantara12/micro/v3/service/broker"
    27  	"github.com/tickoalcantara12/micro/v3/service/context/metadata"
    28  	"github.com/tickoalcantara12/micro/v3/service/errors"
    29  	"github.com/tickoalcantara12/micro/v3/service/logger"
    30  	"github.com/tickoalcantara12/micro/v3/service/registry"
    31  	"github.com/tickoalcantara12/micro/v3/service/server"
    32  )
    33  
    34  const (
    35  	subSig = "func(context.Context, interface{}) error"
    36  )
    37  
    38  type handler struct {
    39  	method  reflect.Value
    40  	reqType reflect.Type
    41  	ctxType reflect.Type
    42  }
    43  
    44  type subscriber struct {
    45  	topic      string
    46  	rcvr       reflect.Value
    47  	typ        reflect.Type
    48  	subscriber interface{}
    49  	handlers   []*handler
    50  	endpoints  []*registry.Endpoint
    51  	opts       server.SubscriberOptions
    52  }
    53  
    54  func newSubscriber(topic string, sub interface{}, opts ...server.SubscriberOption) server.Subscriber {
    55  	options := server.SubscriberOptions{
    56  		AutoAck: true,
    57  	}
    58  
    59  	for _, o := range opts {
    60  		o(&options)
    61  	}
    62  
    63  	var endpoints []*registry.Endpoint
    64  	var handlers []*handler
    65  
    66  	if typ := reflect.TypeOf(sub); typ.Kind() == reflect.Func {
    67  		h := &handler{
    68  			method: reflect.ValueOf(sub),
    69  		}
    70  
    71  		switch typ.NumIn() {
    72  		case 1:
    73  			h.reqType = typ.In(0)
    74  		case 2:
    75  			h.ctxType = typ.In(0)
    76  			h.reqType = typ.In(1)
    77  		}
    78  
    79  		handlers = append(handlers, h)
    80  
    81  		endpoints = append(endpoints, &registry.Endpoint{
    82  			Name:    "Func",
    83  			Request: extractSubValue(typ),
    84  			Metadata: map[string]string{
    85  				"topic":      topic,
    86  				"subscriber": "true",
    87  			},
    88  		})
    89  	} else {
    90  		hdlr := reflect.ValueOf(sub)
    91  		name := reflect.Indirect(hdlr).Type().Name()
    92  
    93  		for m := 0; m < typ.NumMethod(); m++ {
    94  			method := typ.Method(m)
    95  			h := &handler{
    96  				method: method.Func,
    97  			}
    98  
    99  			switch method.Type.NumIn() {
   100  			case 2:
   101  				h.reqType = method.Type.In(1)
   102  			case 3:
   103  				h.ctxType = method.Type.In(1)
   104  				h.reqType = method.Type.In(2)
   105  			}
   106  
   107  			handlers = append(handlers, h)
   108  
   109  			endpoints = append(endpoints, &registry.Endpoint{
   110  				Name:    name + "." + method.Name,
   111  				Request: extractSubValue(method.Type),
   112  				Metadata: map[string]string{
   113  					"topic":      topic,
   114  					"subscriber": "true",
   115  				},
   116  			})
   117  		}
   118  	}
   119  
   120  	return &subscriber{
   121  		rcvr:       reflect.ValueOf(sub),
   122  		typ:        reflect.TypeOf(sub),
   123  		topic:      topic,
   124  		subscriber: sub,
   125  		handlers:   handlers,
   126  		endpoints:  endpoints,
   127  		opts:       options,
   128  	}
   129  }
   130  
   131  func validateSubscriber(sub server.Subscriber) error {
   132  	typ := reflect.TypeOf(sub.Subscriber())
   133  	var argType reflect.Type
   134  
   135  	if typ.Kind() == reflect.Func {
   136  		name := "Func"
   137  		switch typ.NumIn() {
   138  		case 2:
   139  			argType = typ.In(1)
   140  		default:
   141  			return fmt.Errorf("subscriber %v takes wrong number of args: %v required signature %s", name, typ.NumIn(), subSig)
   142  		}
   143  		if !isExportedOrBuiltinType(argType) {
   144  			return fmt.Errorf("subscriber %v argument type not exported: %v", name, argType)
   145  		}
   146  		if typ.NumOut() != 1 {
   147  			return fmt.Errorf("subscriber %v has wrong number of outs: %v require signature %s",
   148  				name, typ.NumOut(), subSig)
   149  		}
   150  		if returnType := typ.Out(0); returnType != typeOfError {
   151  			return fmt.Errorf("subscriber %v returns %v not error", name, returnType.String())
   152  		}
   153  	} else {
   154  		hdlr := reflect.ValueOf(sub.Subscriber())
   155  		name := reflect.Indirect(hdlr).Type().Name()
   156  
   157  		for m := 0; m < typ.NumMethod(); m++ {
   158  			method := typ.Method(m)
   159  
   160  			switch method.Type.NumIn() {
   161  			case 3:
   162  				argType = method.Type.In(2)
   163  			default:
   164  				return fmt.Errorf("subscriber %v.%v takes wrong number of args: %v required signature %s",
   165  					name, method.Name, method.Type.NumIn(), subSig)
   166  			}
   167  
   168  			if !isExportedOrBuiltinType(argType) {
   169  				return fmt.Errorf("%v argument type not exported: %v", name, argType)
   170  			}
   171  			if method.Type.NumOut() != 1 {
   172  				return fmt.Errorf(
   173  					"subscriber %v.%v has wrong number of outs: %v require signature %s",
   174  					name, method.Name, method.Type.NumOut(), subSig)
   175  			}
   176  			if returnType := method.Type.Out(0); returnType != typeOfError {
   177  				return fmt.Errorf("subscriber %v.%v returns %v not error", name, method.Name, returnType.String())
   178  			}
   179  		}
   180  	}
   181  
   182  	return nil
   183  }
   184  
   185  func (g *grpcServer) createSubHandler(sb *subscriber, opts server.Options) broker.Handler {
   186  	return func(msg *broker.Message) (err error) {
   187  
   188  		defer func() {
   189  			if r := recover(); r != nil {
   190  				if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
   191  					logger.Error("panic recovered: ", r)
   192  					logger.Error(string(debug.Stack()))
   193  				}
   194  				err = errors.InternalServerError(g.opts.Name+".subscriber", "panic recovered: %v", r)
   195  			}
   196  		}()
   197  
   198  		// if we don't have headers, create empty map
   199  		if msg.Header == nil {
   200  			msg.Header = make(map[string]string)
   201  		}
   202  
   203  		ct := msg.Header["Content-Type"]
   204  		if len(ct) == 0 {
   205  			msg.Header["Content-Type"] = defaultContentType
   206  			ct = defaultContentType
   207  		}
   208  		cf, err := g.newGRPCCodec(ct)
   209  		if err != nil {
   210  			return err
   211  		}
   212  
   213  		hdr := make(map[string]string, len(msg.Header))
   214  		for k, v := range msg.Header {
   215  			hdr[k] = v
   216  		}
   217  		delete(hdr, "Content-Type")
   218  		ctx := metadata.NewContext(context.Background(), hdr)
   219  
   220  		results := make(chan error, len(sb.handlers))
   221  
   222  		for i := 0; i < len(sb.handlers); i++ {
   223  			handler := sb.handlers[i]
   224  
   225  			var isVal bool
   226  			var req reflect.Value
   227  
   228  			if handler.reqType.Kind() == reflect.Ptr {
   229  				req = reflect.New(handler.reqType.Elem())
   230  			} else {
   231  				req = reflect.New(handler.reqType)
   232  				isVal = true
   233  			}
   234  			if isVal {
   235  				req = req.Elem()
   236  			}
   237  
   238  			if err = cf.Unmarshal(msg.Body, req.Interface()); err != nil {
   239  				return err
   240  			}
   241  
   242  			fn := func(ctx context.Context, msg server.Message) error {
   243  				var vals []reflect.Value
   244  				if sb.typ.Kind() != reflect.Func {
   245  					vals = append(vals, sb.rcvr)
   246  				}
   247  				if handler.ctxType != nil {
   248  					vals = append(vals, reflect.ValueOf(ctx))
   249  				}
   250  
   251  				vals = append(vals, reflect.ValueOf(msg.Payload()))
   252  
   253  				returnValues := handler.method.Call(vals)
   254  				if rerr := returnValues[0].Interface(); rerr != nil {
   255  					return rerr.(error)
   256  				}
   257  				return nil
   258  			}
   259  
   260  			for i := len(opts.SubWrappers); i > 0; i-- {
   261  				fn = opts.SubWrappers[i-1](fn)
   262  			}
   263  
   264  			if g.wg != nil {
   265  				g.wg.Add(1)
   266  			}
   267  			go func() {
   268  				if g.wg != nil {
   269  					defer g.wg.Done()
   270  				}
   271  				err := fn(ctx, &rpcMessage{
   272  					topic:       sb.topic,
   273  					contentType: ct,
   274  					payload:     req.Interface(),
   275  					header:      msg.Header,
   276  					body:        msg.Body,
   277  				})
   278  				results <- err
   279  			}()
   280  		}
   281  		var errors []string
   282  		for i := 0; i < len(sb.handlers); i++ {
   283  			if rerr := <-results; rerr != nil {
   284  				errors = append(errors, rerr.Error())
   285  			}
   286  		}
   287  		if len(errors) > 0 {
   288  			err = fmt.Errorf("subscriber error: %s", strings.Join(errors, "\n"))
   289  		}
   290  
   291  		return err
   292  	}
   293  }
   294  
   295  func (s *subscriber) Topic() string {
   296  	return s.topic
   297  }
   298  
   299  func (s *subscriber) Subscriber() interface{} {
   300  	return s.subscriber
   301  }
   302  
   303  func (s *subscriber) Endpoints() []*registry.Endpoint {
   304  	return s.endpoints
   305  }
   306  
   307  func (s *subscriber) Options() server.SubscriberOptions {
   308  	return s.opts
   309  }