Repository navigation
Expand file tree
/
Copy pathcontext_next.go
More file actions
170 lines (155 loc) · 5.64 KB
/
Copy pathcontext_next.go
File metadata and controls
170 lines (155 loc) · 5.64 KB
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
160
161
162
163
164
165
166
167
168
169
170
package velaros
import (
"fmt"
"reflect"
"runtime/debug"
"strings"
)
// Next continues execution to the next handler in the chain. Middleware
// should call Next() to pass control to subsequent handlers, then perform
// any post-processing after Next() returns.
//
// If an error is set on the context (via Error field or panic), subsequent
// handlers are skipped. Next() is safe to call multiple times.
func (c *Context) Next() {
// In the case that this is a sub context, we need to update the parent
// context with the current context's state.
defer c.tryUpdateParent()
// For BindClose handlers, we don't want to check if the socket is closed
// since they're meant to run during the close process
isCloseHandler := c.currentHandlerNode != nil && c.currentHandlerNode.BindType == CloseBindType
if c.Error != nil || (!isCloseHandler && c.socket.IsClosed()) {
return
}
// Auto-create interceptor if message has an ID. The first message with a given ID
// creates the interceptor, subsequent messages with the same ID get intercepted.
if c.message.hasSetID {
interceptorChan, ok := c.socket.GetInterceptor(c.message.ID)
if !ok {
// First message with this ID - create interceptor
interceptorChan = make(chan *InboundMessage, 1)
c.interceptorChan = interceptorChan
c.ownsInterceptor = true
c.socket.AddInterceptor(c.message.ID, interceptorChan)
// Don't push message yet - it will be available via c.message for now
} else if c.interceptorChan == nil {
// Subsequent message with this ID - intercept it (unless this context owns the interceptor)
select {
case interceptorChan <- c.message:
c.message = nil // Prevent double-free
return
case <-c.socket.Done():
// Socket closed while trying to send - free message and continue
c.message.free()
return
}
}
c.message.hasSetID = false
}
// if the path was set, and have a matching node, make sure it still matches
// the path, otherwise clear it and move on to the next node.
if c.message.hasSetPath {
if c.currentHandlerNodeMatches && !c.currentHandlerNode.tryMatch(c) {
c.currentHandlerNode = c.currentHandlerNode.Next
c.currentHandlerNodeMatches = false
c.currentHandlerIndex = 0
c.currentHandler = nil
}
c.message.hasSetPath = false
}
// walk the chain looking for a handler with a pattern that matches the path
// of the request, or until we reach the end of the chain
for c.currentHandlerNode != nil {
// Because handlers can have multiple handler functions,
// we may save a matching handler node to the context so that we can
// continue from the same handler until we have executed all of its
// handlers.
//
// If we do not have a matching handler node, we will walk the chain
// until we find a matching handler node.
if !c.currentHandlerNodeMatches {
for c.currentHandlerNode != nil {
if c.currentHandlerNode.tryMatch(c) {
c.currentHandlerNodeMatches = true
break
}
c.currentHandlerNode = c.currentHandlerNode.Next
}
if !c.currentHandlerNodeMatches {
break
}
}
// Grab a handler function from the matching handler node.
// If there are more than one, we will continue from the same handler node
// the next time Next is called. We iterate through the handler functions
// until we have executed all of them.
if c.currentHandlerIndex < len(c.currentHandlerNode.Handlers) {
c.currentHandler = c.currentHandlerNode.Handlers[c.currentHandlerIndex]
c.currentHandlerIndex += 1
break
}
// We only get here if we had a matching handler node, and we have
// executed all of its handlers. We can now clear the
// matching handler node, and continue to the next handler node.
c.currentHandlerNode = c.currentHandlerNode.Next
c.currentHandlerNodeMatches = false
c.currentHandlerIndex = 0
c.currentHandler = nil
}
// If we didn't find a handler function and we have reached
// the end of the chain, we can return early.
if c.currentHandler == nil {
return
}
// Execute the handler function. Throw an error if it's not
// an expected type.
bindType := c.currentHandlerNode.BindType
if currentHandler, ok := c.currentHandler.(OpenHandler); ok && bindType == OpenBindType {
execWithCtxRecovery(c, func() {
currentHandler.HandleOpen(c)
})
} else if currentHandler, ok := c.currentHandler.(CloseHandler); ok && bindType == CloseBindType {
execWithCtxRecovery(c, func() {
currentHandler.HandleClose(c)
})
} else if currentHandler, ok := c.currentHandler.(Handler); ok && bindType == NormalBindType {
execWithCtxRecovery(c, func() {
currentHandler.Handle(c)
})
} else if currentHandler, ok := c.currentHandler.(HandlerFunc); ok {
execWithCtxRecovery(c, func() {
currentHandler(c)
})
} else if currentHandler, ok := c.currentHandler.(func(*Context)); ok {
execWithCtxRecovery(c, func() {
currentHandler(c)
})
} else {
panic(fmt.Sprintf("Unknown handler type: %s", reflect.TypeOf(c.currentHandler)))
}
// Call next automatically for open and close handlers,
// but only if there was no error and the socket is not closed
if (bindType == OpenBindType && !c.socket.IsClosed()) || bindType == CloseBindType {
c.Next()
}
// Prevent handlers from calling Next twice
c.currentHandlerNode = nil
c.currentHandlerNodeMatches = false
c.currentHandlerIndex = 0
c.currentHandler = nil
}
func execWithCtxRecovery(ctx *Context, fn func()) {
defer func() {
if maybeErr := recover(); maybeErr != nil {
if err, ok := maybeErr.(error); ok {
ctx.Error = err
} else {
ctx.Error = fmt.Errorf("%s", maybeErr)
}
stack := string(debug.Stack())
stackLines := strings.Split(stack, "\n")
ctx.ErrorStack = strings.Join(stackLines[6:], "\n")
}
}()
fn()
}