-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathserver.go
105 lines (91 loc) · 2.81 KB
/
server.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
package http
import (
"context"
"fmt"
"reflect"
"unicode"
"unicode/utf8"
"go.unistack.org/micro/v3/server"
)
type methodType struct {
ArgType reflect.Type
ReplyType reflect.Type
ContextType reflect.Type
method reflect.Method
stream bool
}
// Is this an exported - upper case - name?
func isExported(name string) bool {
r, _ := utf8.DecodeRuneInString(name)
return unicode.IsUpper(r)
}
// Is this type exported or a builtin?
func isExportedOrBuiltinType(t reflect.Type) bool {
for t.Kind() == reflect.Ptr {
t = t.Elem()
}
// PkgPath will be non-empty even for an exported type,
// so we need to check the type name as well.
return isExported(t.Name()) || t.PkgPath() == ""
}
// prepareEndpoint() returns a methodType for the provided method or nil
// in case if the method was unsuitable.
func prepareEndpoint(method reflect.Method) (*methodType, error) {
mtype := method.Type
mname := method.Name
var replyType, argType, contextType reflect.Type
var stream bool
// Endpoint() must be exported.
if method.PkgPath != "" {
return nil, fmt.Errorf("Endpoint must be exported")
}
switch mtype.NumIn() {
case 3:
// assuming streaming
argType = mtype.In(2)
contextType = mtype.In(1)
stream = true
case 4:
// method that takes a context
argType = mtype.In(2)
replyType = mtype.In(3)
contextType = mtype.In(1)
default:
return nil, fmt.Errorf("method %v of %v has wrong number of ins: %v", mname, mtype, mtype.NumIn())
}
switch stream {
case true:
// check stream type
streamType := reflect.TypeOf((*server.Stream)(nil)).Elem()
if !argType.Implements(streamType) {
return nil, fmt.Errorf("%v argument does not implement Streamer interface: %v", mname, argType)
}
default:
// First arg need not be a pointer.
if !isExportedOrBuiltinType(argType) {
return nil, fmt.Errorf("%v argument type not exported: %v", mname, argType)
}
if replyType.Kind() != reflect.Ptr {
return nil, fmt.Errorf("method %v reply type not a pointer: %v", mname, replyType)
}
// Reply type must be exported.
if !isExportedOrBuiltinType(replyType) {
return nil, fmt.Errorf("method %v reply type not exported: %v", mname, replyType)
}
}
// Endpoint() needs one out.
if mtype.NumOut() != 1 {
return nil, fmt.Errorf("method %v has wrong number of outs: %v", mname, mtype.NumOut())
}
// The return type of the method must be error.
if returnType := mtype.Out(0); returnType != typeOfError {
return nil, fmt.Errorf("method %v returns %v not error", mname, returnType.String())
}
return &methodType{method: method, ArgType: argType, ReplyType: replyType, ContextType: contextType, stream: stream}, nil
}
func (m *methodType) prepareContext(ctx context.Context) reflect.Value {
if contextv := reflect.ValueOf(ctx); contextv.IsValid() {
return contextv
}
return reflect.Zero(m.ContextType)
}