-
Notifications
You must be signed in to change notification settings - Fork 4.6k
/
registry.go
138 lines (115 loc) · 4.33 KB
/
registry.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
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
package registry
import (
"bytes"
"context"
"reflect"
"strings"
"sync"
"github.com/golang/protobuf/jsonpb"
"github.com/golang/protobuf/proto"
protov2 "google.golang.org/protobuf/proto"
"github.com/v2fly/v2ray-core/v5/common/protoext"
"github.com/v2fly/v2ray-core/v5/common/protofilter"
"github.com/v2fly/v2ray-core/v5/common/serial"
)
type implementationRegistry struct {
implSet map[string]*implementationSet
}
func (i *implementationRegistry) RegisterImplementation(name string, opt *protoext.MessageOpt, loader CustomLoader) {
interfaceType := opt.GetType()
for _, v := range interfaceType {
i.registerSingleImplementation(v, name, opt, loader)
}
}
func (i *implementationRegistry) registerSingleImplementation(interfaceType, name string, opt *protoext.MessageOpt, loader CustomLoader) {
implSet, found := i.implSet[interfaceType]
if !found {
implSet = newImplementationSet()
i.implSet[interfaceType] = implSet
}
implSet.RegisterImplementation(name, opt, loader)
}
func (i *implementationRegistry) findImplementationByAlias(interfaceType, alias string) (string, CustomLoader, error) {
implSet, found := i.implSet[interfaceType]
if !found {
return "", nil, newError("cannot find implemention unknown interface type")
}
return implSet.findImplementationByAlias(alias)
}
func (i *implementationRegistry) LoadImplementationByAlias(ctx context.Context, interfaceType, alias string, data []byte) (proto.Message, error) {
var implementationFullName string
if strings.HasPrefix(alias, "#") {
// skip resolution for full name
implementationFullName, _ = strings.CutPrefix(alias, "#")
} else {
registryResult, customLoader, err := i.findImplementationByAlias(interfaceType, alias)
if err != nil {
return nil, newError("unable to find implementation").Base(err)
}
if customLoader != nil {
return customLoader(data, i)
}
implementationFullName = registryResult
}
implementationConfigInstance, err := serial.GetInstance(implementationFullName)
if err != nil {
return nil, newError("unable to create implementation config instance").Base(err)
}
unmarshaler := jsonpb.Unmarshaler{AllowUnknownFields: false}
err = unmarshaler.Unmarshal(bytes.NewReader(data), implementationConfigInstance.(proto.Message))
if err != nil {
return nil, newError("unable to parse json content").Base(err)
}
implementationConfigInstancev2 := proto.MessageV2(implementationConfigInstance)
if isRestrictedModeContext(ctx) {
if err := enforceRestriction(implementationConfigInstancev2); err != nil {
return nil, err
}
}
if err := protofilter.FilterProtoConfig(ctx, implementationConfigInstancev2); err != nil {
return nil, err
}
return implementationConfigInstance.(proto.Message), nil
}
func newImplementationRegistry() *implementationRegistry {
return &implementationRegistry{implSet: map[string]*implementationSet{}}
}
var globalImplementationRegistry = newImplementationRegistry()
var initialized = &sync.Once{}
type registerRequest struct {
proto interface{}
loader CustomLoader
}
var registerRequests []registerRequest
// RegisterImplementation register an implementation of a type of interface
// loader(CustomLoader) is a private API, its interface is subject to breaking changes
func RegisterImplementation(proto interface{}, loader CustomLoader) error {
registerRequests = append(registerRequests, registerRequest{
proto: proto,
loader: loader,
})
return nil
}
func registerImplementation(proto interface{}, loader CustomLoader) error {
protoReflect := reflect.New(reflect.TypeOf(proto).Elem())
proto2 := protoReflect.Interface().(protov2.Message)
msgDesc := proto2.ProtoReflect().Descriptor()
fullName := string(msgDesc.FullName())
msgOpts, err := protoext.GetMessageOptions(msgDesc)
if err != nil {
return newError("unable to find message options").Base(err)
}
globalImplementationRegistry.RegisterImplementation(fullName, msgOpts, loader)
return nil
}
type LoadByAlias interface {
LoadImplementationByAlias(ctx context.Context, interfaceType, alias string, data []byte) (proto.Message, error)
}
func LoadImplementationByAlias(ctx context.Context, interfaceType, alias string, data []byte) (proto.Message, error) {
initialized.Do(func() {
for _, v := range registerRequests {
registerImplementation(v.proto, v.loader)
}
})
return globalImplementationRegistry.LoadImplementationByAlias(ctx, interfaceType, alias, data)
}