forked from albertrdixon/romulus
-
Notifications
You must be signed in to change notification settings - Fork 0
/
middleware.go
136 lines (117 loc) · 3.55 KB
/
middleware.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
package plugin
import (
"encoding/json"
"fmt"
"net/http"
"reflect"
"github.com/codegangsta/cli"
"github.com/timelinelabs/vulcand/router"
"github.com/vulcand/route"
)
// Middleware specification, used to construct new middlewares and plug them into CLI API and backends
type MiddlewareSpec struct {
Type string
// Reader function that returns a middleware from another middleware structure
FromOther interface{}
// Flags for CLI tool to generate interface
CliFlags []cli.Flag
// Function that construtcs a middleware from CLI parameters
FromCli CliReader
}
func (ms *MiddlewareSpec) FromJSON(data []byte) (Middleware, error) {
// Get a function's type
fnType := reflect.TypeOf(ms.FromOther)
// Create a pointer to the function's first argument
ptr := reflect.New(fnType.In(0)).Interface()
err := json.Unmarshal(data, &ptr)
if err != nil {
return nil, fmt.Errorf("failed to decode %T from JSON, error: %s", ptr, err)
}
// Now let's call the function to produce a middleware
fnVal := reflect.ValueOf(ms.FromOther)
results := fnVal.Call([]reflect.Value{reflect.ValueOf(ptr).Elem()})
m, out := results[0].Interface(), results[1].Interface()
if out != nil {
return nil, out.(error)
}
return m.(Middleware), nil
}
type Middleware interface {
NewHandler(http.Handler) (http.Handler, error)
}
// Reader constructs the middleware from the CLI interface
type CliReader func(c *cli.Context) (Middleware, error)
// Function that returns middleware spec by it's type
type SpecGetter func(string) *MiddlewareSpec
// Registry contains currently registered middlewares and used to support pluggable middlewares across all modules of the vulcand
type Registry struct {
specs []*MiddlewareSpec
notFound Middleware
router router.Router
}
func NewRegistry() *Registry {
return &Registry{
specs: []*MiddlewareSpec{},
router: route.NewMux(),
}
}
func (r *Registry) AddSpec(s *MiddlewareSpec) error {
if s == nil {
return fmt.Errorf("spec can not be nil")
}
if r.GetSpec(s.Type) != nil {
return fmt.Errorf("middleware of type %s already registered", s.Type)
}
if err := verifySignature(s.FromOther); err != nil {
return err
}
r.specs = append(r.specs, s)
return nil
}
func (r *Registry) GetSpec(middlewareType string) *MiddlewareSpec {
for _, s := range r.specs {
if s.Type == middlewareType {
return s
}
}
return nil
}
func (r *Registry) GetSpecs() []*MiddlewareSpec {
return r.specs
}
func (r *Registry) AddNotFoundMiddleware(notFound Middleware) error {
r.notFound = notFound
return nil
}
func (r *Registry) GetNotFoundMiddleware() Middleware {
return r.notFound
}
func (r *Registry) SetRouter(router router.Router) error {
r.router = router
return nil
}
func (r *Registry) GetRouter() router.Router {
return r.router
}
func verifySignature(fn interface{}) error {
t := reflect.TypeOf(fn)
if t == nil || t.Kind() != reflect.Func {
return fmt.Errorf("expected function, got %s", t)
}
if t.NumIn() != 1 {
return fmt.Errorf("expected function with one input argument, got %d", t.NumIn())
}
if t.In(0).Kind() != reflect.Struct {
return fmt.Errorf("function argument should be struct, got %s", t.In(0).Kind())
}
if t.NumOut() != 2 {
return fmt.Errorf("function should return 2 values, got %d", t.NumOut())
}
if !t.Out(0).AssignableTo(reflect.TypeOf((*Middleware)(nil)).Elem()) {
return fmt.Errorf("function first return value should be Middleware got, %s", t.Out(0))
}
if !t.Out(1).AssignableTo(reflect.TypeOf((*error)(nil)).Elem()) {
return fmt.Errorf("function second return value should be error got, %s", t.Out(1))
}
return nil
}