-
Notifications
You must be signed in to change notification settings - Fork 12
/
registry.go
159 lines (139 loc) · 4.68 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
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
package kratos
import (
"fmt"
"regexp"
"sync"
"github.com/goph/emperror"
"github.com/xmidt-org/wrp-go/v3"
)
// ErrNoDownstreamHandler provides an easy way for a consumer to do logic based
// on the error of no downstream handler being found.
type ErrNoDownstreamHandler interface {
ErrNoDownstreamHandler()
}
// errNoDownstreamHandler is a sentinel error that implements ErrNoDownstreamHandler.
type errNoDownstreamHandler struct {
}
// ErrNoDownstreamHandler is a throwaway function to implement the interface,
// making it possible to assert the type of the errNoDownstreamHandler.
func (e errNoDownstreamHandler) ErrNoDownstreamHandler() {}
// Error ensures that errNoDownstreamHandler is an error.
func (e errNoDownstreamHandler) Error() string {
return "no downstream handler found for provided destination"
}
// ErrInvalidHandler provides an easy way for a consumer to do logic based on
// the error of an invalid handler being found.
type ErrInvalidHandler interface {
ErrInvalidHandler()
}
// errInvalidHandler is a sentinel error that implements ErrInvalidHandler.
type errInvalidHandler struct {
}
// ErrInvalidHandler is a throwaway function to implement the interface, making
// it possible to assert the type of the errInvalidHandler.
func (e errInvalidHandler) ErrInvalidHandler() {}
// Error ensures that errInvalidHandler is an error.
func (e errInvalidHandler) Error() string {
return "handler cannot be nil"
}
// HandlerConfig is the values that a consumer can set that specify the handler
// to use for the regular expression.
type HandlerConfig struct {
Regexp string
Handler DownstreamHandler
}
// HandlerGroup is an internal data type for Client interface
// that helps keep track of registered handler functions.
type HandlerGroup struct {
keyRegex *regexp.Regexp
handler DownstreamHandler
}
// DownstreamHandler should be implemented by the user so that they
// may deal with received messages how they please.
type DownstreamHandler interface {
HandleMessage(msg *wrp.Message) *wrp.Message
Close()
}
// HandlerRegistry is an interface that handles adding, getting, and removing
// DownstreamHandlers.
type HandlerRegistry interface {
Add(string, DownstreamHandler) error
Remove(string)
GetHandler(string) (DownstreamHandler, error)
Close()
}
// handlerRegistry is our implementation for HandlerRegistry that can be used
// concurrently.
type handlerRegistry struct {
store map[string]HandlerGroup
lock sync.RWMutex
}
// NewHandlerRegistry creates a handlerRegistry based on the initial handlers
// given.
func NewHandlerRegistry(config []HandlerConfig) (*handlerRegistry, error) {
registry := handlerRegistry{
store: make(map[string]HandlerGroup),
}
errs := errorList{}
for _, c := range config {
if c.Handler == nil {
errs = append(errs, emperror.Wrap(errInvalidHandler{}, fmt.Sprintf("cannot add handler for regular expression [%v]", c.Regexp)))
continue
}
r, err := regexp.Compile(c.Regexp)
if err != nil {
errs = append(errs, emperror.Wrap(err, fmt.Sprintf("failed to compile regular expression [%v]", c.Regexp)))
} else {
registry.store[c.Regexp] = HandlerGroup{keyRegex: r, handler: c.Handler}
}
}
if len(errs) == 0 {
return ®istry, nil
}
return ®istry, errs
}
// Add provides a way to add a new handler to a pre-existing handlerRegistry.
// If there is already a handler for the regular expression given, it is
// overwritten with the new handler.
func (h *handlerRegistry) Add(regexpName string, handler DownstreamHandler) error {
if handler == nil {
return errInvalidHandler{}
}
h.lock.Lock()
defer h.lock.Unlock()
r, err := regexp.Compile(regexpName)
if err != nil {
return emperror.WrapWith(err, "failed to compile regular expression", "regexp", regexpName)
}
h.store[regexpName] = HandlerGroup{keyRegex: r, handler: handler}
return nil
}
// Remove provides a way to remove an already existing handler in the
// handlerRegistry.
func (h *handlerRegistry) Remove(regexpName string) {
h.lock.Lock()
delete(h.store, regexpName)
h.lock.Unlock()
}
// GetHandler gives the handler whose regular expression matches the
// destination given. If there is no handler with a matching regular
// expression, an error is returned.
func (h *handlerRegistry) GetHandler(destination string) (DownstreamHandler, error) {
h.lock.RLock()
defer h.lock.RUnlock()
for _, handler := range h.store {
if handler.keyRegex.MatchString(destination) {
return handler.handler, nil
}
}
return nil, errNoDownstreamHandler{}
}
// Close calls the Close function on all the handlers in the handlerRegistry.
func (h *handlerRegistry) Close() {
h.lock.Lock()
defer h.lock.Unlock()
for key, handler := range h.store {
handler.handler.Close()
delete(h.store, key)
}
}