forked from uber/tchannel-go
-
Notifications
You must be signed in to change notification settings - Fork 3
/
handlers.go
160 lines (134 loc) · 5.45 KB
/
handlers.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
160
// Copyright (c) 2015 Uber Technologies, Inc.
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
package tchannel
import (
"reflect"
"runtime"
"sync"
"golang.org/x/net/context"
)
// A Handler is an object that can be registered with a Channel to process
// incoming calls for a given service and method
type Handler interface {
// Handles an incoming call for service
Handle(ctx context.Context, call *InboundCall)
}
// registrar is a subset of the Registrar interface, only containing Register.
type registrar interface {
Register(h Handler, methodName string)
}
// A HandlerFunc is an adapter to allow the use of ordinary functions as
// Channel handlers. If f is a function with the appropriate signature, then
// HandlerFunc(f) is a Handler object that calls f.
type HandlerFunc func(ctx context.Context, call *InboundCall)
// Handle calls f(ctx, call)
func (f HandlerFunc) Handle(ctx context.Context, call *InboundCall) { f(ctx, call) }
// An ErrorHandlerFunc is an adapter to allow the use of ordinary functions as
// Channel handlers, with error handling convenience. If f is a function with
// the appropriate signature, then ErrorHandlerFunc(f) is a Handler object that
// calls f.
type ErrorHandlerFunc func(ctx context.Context, call *InboundCall) error
// Handle calls f(ctx, call)
func (f ErrorHandlerFunc) Handle(ctx context.Context, call *InboundCall) {
if err := f(ctx, call); err != nil {
if GetSystemErrorCode(err) == ErrCodeUnexpected {
call.log.WithFields(f.getLogFields()...).WithFields(ErrField(err)).Error("Unexpected handler error")
}
call.Response().SendSystemError(err)
}
}
func (f ErrorHandlerFunc) getLogFields() LogFields {
ptr := reflect.ValueOf(f).Pointer()
handlerFunc := runtime.FuncForPC(ptr) // can't be nil
fileName, fileLine := handlerFunc.FileLine(ptr)
return LogFields{
{"handlerFuncName", handlerFunc.Name()},
{"handlerFuncFileName", fileName},
{"handlerFuncFileLine", fileLine},
}
}
// Manages handlers
type handlerMap struct {
sync.RWMutex
handlers map[string]Handler
}
// Register implements registrar.
func (hmap *handlerMap) Register(h Handler, method string) {
hmap.Lock()
defer hmap.Unlock()
if hmap.handlers == nil {
hmap.handlers = make(map[string]Handler)
}
hmap.handlers[method] = h
}
// Finds the handler matching the given service and method. See https://github.com/golang/go/issues/3512
// for the reason that method is []byte instead of a string
func (hmap *handlerMap) find(method []byte) Handler {
hmap.RLock()
handler := hmap.handlers[string(method)]
hmap.RUnlock()
return handler
}
func (hmap *handlerMap) Handle(ctx context.Context, call *InboundCall) {
c := call.conn
h := hmap.find(call.Method())
if h == nil {
c.log.WithFields(
LogField{"serviceName", call.ServiceName()},
LogField{"method", call.MethodString()},
).Error("Couldn't find handler.")
call.Response().SendSystemError(
NewSystemError(ErrCodeBadRequest, "no handler for service %q and method %q", call.ServiceName(), call.Method()))
return
}
if c.log.Enabled(LogLevelDebug) {
c.log.Debugf("Dispatching %s:%s from %s", call.ServiceName(), call.Method(), c.remotePeerInfo)
}
h.Handle(ctx, call)
}
// channelHandler is a Handler that wraps a Channel and delegates requests
// to SubChannels based on the inbound call's service name.
type channelHandler struct{ ch *Channel }
func (c channelHandler) Handle(ctx context.Context, call *InboundCall) {
c.ch.GetSubChannel(call.ServiceName()).handler.Handle(ctx, call)
}
// Register registers the handler on the channel's default service name.
func (c channelHandler) Register(h Handler, methodName string) {
c.ch.GetSubChannel(c.ch.PeerInfo().ServiceName).Register(h, methodName)
}
// userHandlerWithSkip is a Handler that wraps a localHandler backed by the channel.
// and a user provided handler.
// The inbound call will be handled by user handler, unless the call's
// service and method name are configured to be handled by localHandler
// from ignore.
type userHandlerWithSkip struct {
localHandler channelHandler
ignoreUserHandler map[string]map[string]struct{} // key is service, subkey is method
userHandler Handler
}
func (u userHandlerWithSkip) Handle(ctx context.Context, call *InboundCall) {
if _, ok := u.ignoreUserHandler[call.ServiceName()][call.MethodString()]; ok {
u.localHandler.Handle(ctx, call)
return
}
u.userHandler.Handle(ctx, call)
}
func (u userHandlerWithSkip) Register(h Handler, methodName string) {
u.localHandler.Register(h, methodName)
}