github.com/tickoalcantara12/micro/v3@v3.0.0-20221007104245-9d75b9bcbab9/service/server/grpc/server.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/server.go
    16  
    17  package grpc
    18  
    19  // Copyright 2009 The Go Authors. All rights reserved.
    20  // Use of this source code is governed by a BSD-style
    21  // license that can be found in the LICENSE file.
    22  //
    23  // Meh, we need to get rid of this shit
    24  
    25  import (
    26  	"context"
    27  	"errors"
    28  	"reflect"
    29  	"sync"
    30  	"unicode"
    31  	"unicode/utf8"
    32  
    33  	"github.com/tickoalcantara12/micro/v3/service/logger"
    34  	"github.com/tickoalcantara12/micro/v3/service/server"
    35  )
    36  
    37  var (
    38  	// Precompute the reflect type for error. Can't use error directly
    39  	// because Typeof takes an empty interface value. This is annoying.
    40  	typeOfError = reflect.TypeOf((*error)(nil)).Elem()
    41  )
    42  
    43  type methodType struct {
    44  	method      reflect.Method
    45  	ArgType     reflect.Type
    46  	ReplyType   reflect.Type
    47  	ContextType reflect.Type
    48  	stream      bool
    49  }
    50  
    51  type service struct {
    52  	name   string                 // name of service
    53  	rcvr   reflect.Value          // receiver of methods for the service
    54  	typ    reflect.Type           // type of the receiver
    55  	method map[string]*methodType // registered methods
    56  }
    57  
    58  // server represents an RPC Server.
    59  type rServer struct {
    60  	mu         sync.Mutex // protects the serviceMap
    61  	serviceMap map[string]*service
    62  }
    63  
    64  // Is this an exported - upper case - name?
    65  func isExported(name string) bool {
    66  	rune, _ := utf8.DecodeRuneInString(name)
    67  	return unicode.IsUpper(rune)
    68  }
    69  
    70  // Is this type exported or a builtin?
    71  func isExportedOrBuiltinType(t reflect.Type) bool {
    72  	for t.Kind() == reflect.Ptr {
    73  		t = t.Elem()
    74  	}
    75  	// PkgPath will be non-empty even for an exported type,
    76  	// so we need to check the type name as well.
    77  	return isExported(t.Name()) || t.PkgPath() == ""
    78  }
    79  
    80  // prepareEndpoint() returns a methodType for the provided method or nil
    81  // in case if the method was unsuitable.
    82  func prepareEndpoint(method reflect.Method) *methodType {
    83  	mtype := method.Type
    84  	mname := method.Name
    85  	var replyType, argType, contextType reflect.Type
    86  	var stream bool
    87  
    88  	// Endpoint() must be exported.
    89  	if method.PkgPath != "" {
    90  		return nil
    91  	}
    92  
    93  	switch mtype.NumIn() {
    94  	case 3:
    95  		// assuming streaming
    96  		argType = mtype.In(2)
    97  		contextType = mtype.In(1)
    98  		stream = true
    99  	case 4:
   100  		// method that takes a context
   101  		argType = mtype.In(2)
   102  		replyType = mtype.In(3)
   103  		contextType = mtype.In(1)
   104  	default:
   105  		if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
   106  			logger.Errorf("method %v of %v has wrong number of ins: %v", mname, mtype, mtype.NumIn())
   107  		}
   108  		return nil
   109  	}
   110  
   111  	if stream {
   112  		// check stream type
   113  		streamType := reflect.TypeOf((*server.Stream)(nil)).Elem()
   114  		if !argType.Implements(streamType) {
   115  			if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
   116  				logger.Errorf("%v argument does not implement Streamer interface: %v", mname, argType)
   117  			}
   118  			return nil
   119  		}
   120  	} else {
   121  		// if not stream check the replyType
   122  
   123  		// First arg need not be a pointer.
   124  		if !isExportedOrBuiltinType(argType) {
   125  			if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
   126  				logger.Errorf("%v argument type not exported: %v", mname, argType)
   127  			}
   128  			return nil
   129  		}
   130  
   131  		if replyType.Kind() != reflect.Ptr {
   132  			if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
   133  				logger.Errorf("method %v reply type not a pointer: %v", mname, replyType)
   134  			}
   135  			return nil
   136  		}
   137  
   138  		// Reply type must be exported.
   139  		if !isExportedOrBuiltinType(replyType) {
   140  			if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
   141  				logger.Errorf("method %v reply type not exported: %v", mname, replyType)
   142  			}
   143  			return nil
   144  		}
   145  	}
   146  
   147  	// Endpoint() needs one out.
   148  	if mtype.NumOut() != 1 {
   149  		if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
   150  			logger.Errorf("method %v has wrong number of outs: %v", mname, mtype.NumOut())
   151  		}
   152  		return nil
   153  	}
   154  	// The return type of the method must be error.
   155  	if returnType := mtype.Out(0); returnType != typeOfError {
   156  		if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
   157  			logger.Errorf("method %v returns %v not error", mname, returnType.String())
   158  		}
   159  		return nil
   160  	}
   161  	return &methodType{method: method, ArgType: argType, ReplyType: replyType, ContextType: contextType, stream: stream}
   162  }
   163  
   164  func (server *rServer) register(rcvr interface{}) error {
   165  	server.mu.Lock()
   166  	defer server.mu.Unlock()
   167  	if server.serviceMap == nil {
   168  		server.serviceMap = make(map[string]*service)
   169  	}
   170  	s := new(service)
   171  	s.typ = reflect.TypeOf(rcvr)
   172  	s.rcvr = reflect.ValueOf(rcvr)
   173  	sname := reflect.Indirect(s.rcvr).Type().Name()
   174  	if sname == "" {
   175  		logger.Fatalf("rpc: no service name for type %v", s.typ.String())
   176  	}
   177  	if !isExported(sname) {
   178  		s := "rpc Register: type " + sname + " is not exported"
   179  		if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
   180  			logger.Error(s)
   181  		}
   182  		return errors.New(s)
   183  	}
   184  	if _, present := server.serviceMap[sname]; present {
   185  		return errors.New("rpc: service already defined: " + sname)
   186  	}
   187  	s.name = sname
   188  	s.method = make(map[string]*methodType)
   189  
   190  	// Install the methods
   191  	for m := 0; m < s.typ.NumMethod(); m++ {
   192  		method := s.typ.Method(m)
   193  		if mt := prepareEndpoint(method); mt != nil {
   194  			s.method[method.Name] = mt
   195  		}
   196  	}
   197  
   198  	if len(s.method) == 0 {
   199  		s := "rpc Register: type " + sname + " has no exported methods of suitable type"
   200  		if logger.V(logger.ErrorLevel, logger.DefaultLogger) {
   201  			logger.Error(s)
   202  		}
   203  		return errors.New(s)
   204  	}
   205  	server.serviceMap[s.name] = s
   206  	return nil
   207  }
   208  
   209  func (m *methodType) prepareContext(ctx context.Context) reflect.Value {
   210  	if contextv := reflect.ValueOf(ctx); contextv.IsValid() {
   211  		return contextv
   212  	}
   213  	return reflect.Zero(m.ContextType)
   214  }