-
Notifications
You must be signed in to change notification settings - Fork 16
/
handlers.go
208 lines (187 loc) · 5.69 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
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/*
Copyright (c) Facebook, Inc. and its affiliates.
This source code is licensed under the MIT license found in the
LICENSE file in the root directory of this source tree.
*/
package tacquito
import (
"context"
)
// Writer is an abstraction used for adding Writers to the response object
type Writer interface {
Write(ctx context.Context, p []byte) (int, error)
}
// response implements the Response interface. when testing handlers, provide your own
// mock of this struct via the interface. crypt operations are not exposed for testing.
type response struct {
loggerProvider
ctx context.Context
crypter *crypter
next Handler
// header is the corresponding header that was used to create this response
header Header
// slice of writers to write back the response
writers []Writer
}
// Reply will write the provided EncoderDecoder to the underlying net.Conn. This method handles
// all header values based on the underlying EncoderDecoder. If you want total control on the
// packet that is written, use Send instead.
func (r *response) Reply(v EncoderDecoder) (int, error) {
seqNo := int(r.header.SeqNo)
// some special conditions for different body types
switch t := v.(type) {
case *AuthenReply:
if t.Status == AuthenStatusRestart {
seqNo = 1
} else {
seqNo++
}
default:
seqNo++
}
header := NewHeader(
SetHeaderVersion(r.header.Version),
SetHeaderType(r.header.Type),
SetHeaderSeqNo(seqNo),
SetHeaderFlag(r.header.Flags),
SetHeaderSessionID(r.header.SessionID),
)
b, err := v.MarshalBinary()
if err != nil {
r.Errorf(r.ctx, "unable to marshal packet; %v", err)
return 0, err
}
r.header = *header
p := NewPacket(
SetPacketHeader(header),
SetPacketBody(b),
)
if pbytes, err := p.MarshalBinary(); err == nil {
for _, mw := range r.writers {
_, err := mw.Write(r.ctx, pbytes)
if err != nil {
r.Errorf(r.ctx, "unable to write to response writer; %v", err)
}
}
}
return r.Write(p)
}
// Write will write the packet to the underlying net.Conn. If you are expecting another packet
// to return from the client after writing a response, call Next(handler) to provide a next Handler.
func (r *response) Write(p *Packet) (int, error) {
return r.crypter.write(p)
}
// Next sets the incoming handler to next. This is only used for exchange sequences within the authenticate
// packet types
func (r *response) Next(next Handler) {
r.next = next
}
func (r *response) RegisterWriter(mw Writer) {
r.writers = append(r.writers, mw)
}
func (r *response) Context(ctx context.Context) {
r.ctx = ctx
}
// ReplyWithContext can be used to reply to requests that cause a server error or failure in processing of response.
// This method includes an additional variadic argument `writers` that can be used to write the response `v` to
// other sinks (eg logging backends)
// This method also overwrites the response's context with the supplied `ctx`
func (r *response) ReplyWithContext(ctx context.Context, v EncoderDecoder, writers ...Writer) (int, error) {
r.Context(ctx)
for _, w := range writers {
if w != nil {
r.RegisterWriter(w)
}
}
return r.Reply(v)
}
// Response controls what we send back to the client. Calls to Write should be considered final on the
// packet back to the client. You may not call Exchange after Write.
type Response interface {
Reply(v EncoderDecoder) (int, error)
ReplyWithContext(ctx context.Context, v EncoderDecoder, writers ...Writer) (int, error)
Write(p *Packet) (int, error)
Next(next Handler)
RegisterWriter(Writer)
// Context sets context of response to ctx
Context(ctx context.Context)
}
// Request provides access to the config for this net.Conn and also the packet itself
type Request struct {
Header Header
Body []byte
Context context.Context
}
// Fields will extract all fields from any packet type and attempt to include any optional
// ContextKey values
func (r Request) Fields(keys ...ContextKey) map[string]string {
allFields := r.Header.Fields()
// add optional context values
if r.Context != nil {
for _, key := range keys {
v, ok := r.Context.Value(key).(string)
if ok {
allFields[string(key)] = v
}
}
}
// merge will add our header fields to the body
// the rfc doesn't contain fields that collide
merge := func(a, b map[string]string) {
for k, v := range b {
a[k] = v
}
}
switch r.Header.Type {
case Authenticate:
var as AuthenStart
if err := Unmarshal(r.Body, &as); err == nil {
merge(allFields, as.Fields())
return allFields
}
var ac AuthenContinue
if err := Unmarshal(r.Body, &ac); err == nil {
merge(allFields, ac.Fields())
return allFields
}
var ar AuthenReply
if err := Unmarshal(r.Body, &ar); err == nil {
merge(allFields, ar.Fields())
return allFields
}
case Authorize:
var ar AuthorRequest
if err := Unmarshal(r.Body, &ar); err == nil {
merge(allFields, ar.Fields())
return allFields
}
var arr AuthorReply
if err := Unmarshal(r.Body, &arr); err == nil {
merge(allFields, arr.Fields())
return allFields
}
case Accounting:
var ar AcctRequest
if err := Unmarshal(r.Body, &ar); err == nil {
merge(allFields, ar.Fields())
return allFields
}
var arr AcctReply
if err := Unmarshal(r.Body, &arr); err == nil {
merge(allFields, arr.Fields())
return allFields
}
}
// unknown packet
return nil
}
// Handler form the basis for the state machine during client server exchanges.
type Handler interface {
Handle(response Response, request Request)
}
// HandlerFunc is an adapter that allows higher order functions to be used as Handler interfaces
type HandlerFunc func(response Response, request Request)
// Handle satisfies the Handler interface
func (h HandlerFunc) Handle(response Response, request Request) {
h(response, request)
}