-
Notifications
You must be signed in to change notification settings - Fork 0
/
controller.go
153 lines (136 loc) · 4.73 KB
/
controller.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
package stgin
import (
"mime/multipart"
"net/http"
"time"
)
// Controller is a struct which groups some routes, and may have a path prefix.
// And might hold some request/response/api listeners.
type Controller struct {
Name string
routes []Route
prefix string
requestListeners []RequestListener
responseListeners []ResponseListener
apiListeners []APIListener
interrupts []Interrupt
}
// NewController returns a pointer to a newly created controller with the given name and path prefixes.
func NewController(name string, prefix string) *Controller {
return &Controller{
Name: name,
prefix: "/" + prefix + "/",
}
}
// AddRoutes normalizes, and evaluates path patterns for the given routes, and then adds them to the routes it contains.
func (controller *Controller) AddRoutes(routes ...Route) {
for _, route := range routes {
route.controller = controller
route.Path = normalizePath(controller.prefix + route.Path)
route.correspondingRegex = getRoutePatternRegexOrPanic(route.Path)
controller.routes = append(controller.routes, route)
}
}
// AddRequestListeners registers the given listeners to the controller.
// These listeners then will be applied to all the requests coming inside this controller.
func (controller *Controller) AddRequestListeners(listeners ...RequestListener) {
controller.requestListeners = append(controller.requestListeners, listeners...)
}
// AddResponseListener registers the given listeners to the controller.
// These listeners then will be applied to all the outgoing responses from this controller.
func (controller *Controller) AddResponseListener(listeners ...ResponseListener) {
controller.responseListeners = append(controller.responseListeners, listeners...)
}
// AddAPIListeners registers the given listeners to the controller.
// These listeners then will be applied to all the incoming/outgoing requests and responses after they're evaluated
// And returned to the client.
func (controller *Controller) AddAPIListeners(listeners ...APIListener) {
controller.apiListeners = append(controller.apiListeners, listeners...)
}
// SetTimeout registers a timeout interrupt into the controller.
func (controller *Controller) SetTimeout(timeout time.Duration) {
controller.RegisterInterrupts(TimeoutInterrupt(timeout))
}
// RegisterInterrupts adds the given interrupts to the controller's already existing interrupts.
func (controller *Controller) RegisterInterrupts(interrupts ...Interrupt) {
controller.interrupts = append(controller.interrupts, interrupts...)
}
// executeInternal is just for testing purposes. This simulates executing an actual http request.
func (controller *Controller) executeInternal(request *http.Request) Status {
var headers http.Header
if request.Header == nil {
headers = emptyHeaders
} else {
headers = request.Header
}
rc := RequestContext{
Url: request.URL.Path,
QueryParams: Queries{request.URL.Query()},
PathParams: PathParams{nil},
Headers: headers,
Body: func() *RequestBody {
return &RequestBody{
underlying: nil,
underlyingBytes: []byte{},
hasFilledBytes: false,
}
},
receivedAt: time.Now(),
Method: request.Method,
ContentLength: request.ContentLength,
Host: request.Host,
MultipartForm: func() *multipart.Form {
return request.MultipartForm
},
Scheme: request.URL.Scheme,
RemoteAddr: request.RemoteAddr,
}
interruptChannel := make(chan *Status, 1)
successfulOperationChannel := make(chan *Status, 1)
// panicChannel should not be defined here, since error handling is on server layer
for _, modifier := range controller.requestListeners {
rc = modifier(rc)
}
for _, interrupt := range controller.interrupts {
go interrupt.TriggerFor(rc, interruptChannel)
}
var done bool
go func() {
var result Status
for _, route := range controller.routes {
matches, pathParams := route.acceptsAndPathParams(request)
if matches && acceptsAllQueries(route.expectedQueries, request.URL.Query()) {
rc.PathParams = PathParams{pathParams}
done = true
result = route.Action(rc)
break
}
}
if !done {
result = NotFound(Json(&generalFailureMessage{
StatusCode: 404,
Path: request.URL.Path,
Message: "not found",
Method: request.Method,
}))
}
for _, modifier := range controller.responseListeners {
result = modifier(result)
}
successfulOperationChannel <- &result
}()
select {
case interrupted := <-interruptChannel:
result := *interrupted
for _, watcher := range controller.apiListeners {
go watcher(rc, result)
}
return result
case normal := <-successfulOperationChannel:
result := *normal
for _, watcher := range controller.apiListeners {
go watcher(rc, result)
}
return result
}
}