github.com/swiftstack/ProxyFS@v0.0.0-20210203235616-4017c267d62f/retryrpc/svrmap.go (about)

     1  // Copyright (c) 2015-2021, NVIDIA CORPORATION.
     2  // SPDX-License-Identifier: Apache-2.0
     3  
     4  package retryrpc
     5  
     6  import (
     7  	"errors"
     8  	"reflect"
     9  	"unicode"
    10  	"unicode/utf8"
    11  )
    12  
    13  var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
    14  
    15  // Find all methods for the type which can be exported.
    16  // Build svrMap listing methods available as well as their
    17  // request and reply types.
    18  func (server *Server) buildSvrMap(typ reflect.Type) {
    19  	for m := 0; m < typ.NumMethod(); m++ {
    20  		method := typ.Method(m)
    21  		mtype := method.Type
    22  		mname := method.Name
    23  
    24  		// Just like net/rpc, we have these requirements on methods:
    25  		// - must be exported
    26  		// - needs three ins: receiver, *args, *reply
    27  		// - reply has to be a pointer and must be exported
    28  		// - method can only return one value of type error
    29  		if method.PkgPath != "" {
    30  			continue
    31  		}
    32  
    33  		if mtype.NumIn() != 3 {
    34  			continue
    35  		}
    36  		argType := mtype.In(1)
    37  		if !isExportedOrBuiltinType(argType) {
    38  			continue
    39  		}
    40  
    41  		replyType := mtype.In(2)
    42  		if replyType.Kind() != reflect.Ptr {
    43  			continue
    44  		}
    45  
    46  		if !isExportedOrBuiltinType(replyType) {
    47  			continue
    48  		}
    49  
    50  		if mtype.NumOut() != 1 {
    51  			continue
    52  		}
    53  
    54  		returnType := mtype.Out(0)
    55  		if returnType != typeOfError {
    56  			continue
    57  		}
    58  
    59  		// We save off the request type so we know how to unmarshal the request.
    60  		// We use the reply type to allocate the reply struct and marshal the response.
    61  		ma := methodArgs{methodPtr: &method, request: argType, reply: replyType}
    62  		server.svrMap[mname] = &ma
    63  	}
    64  }
    65  
    66  func isExportedOrBuiltinType(t reflect.Type) bool {
    67  	for t.Kind() == reflect.Ptr {
    68  		t = t.Elem()
    69  	}
    70  	return isMethodExported(t.Name()) || t.PkgPath() == ""
    71  }
    72  
    73  func isMethodExported(name string) bool {
    74  	ch, _ := utf8.DecodeRuneInString(name)
    75  	return unicode.IsUpper(ch)
    76  }
    77  
    78  // Figure out what methods we can have as RPCs and build the
    79  // service map
    80  func (server *Server) register(retrySvr interface{}) (err error) {
    81  
    82  	// Find all the methods associated with retrySvr and put into serviceMap
    83  	typ := reflect.TypeOf(retrySvr)
    84  	rcvr := reflect.ValueOf(retrySvr)
    85  	sname := reflect.Indirect(rcvr).Type().Name()
    86  
    87  	if !isMethodExported(sname) {
    88  		s := "retryrpc.Register: type " + sname + " is not exported"
    89  		return errors.New(s)
    90  	}
    91  
    92  	server.buildSvrMap(typ)
    93  	return
    94  }