forked from chanxuehong/wechat
-
Notifications
You must be signed in to change notification settings - Fork 0
/
context.go
213 lines (188 loc) · 6.57 KB
/
context.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
209
210
211
212
213
package core
import (
"bufio"
"encoding/base64"
"encoding/xml"
"net/http"
"net/url"
"strconv"
"github.com/LaughingVzr/wechat.v2/internal/debug/callback"
"github.com/LaughingVzr/wechat.v2/internal/util"
)
const (
initHandlerIndex = -1
abortHandlerIndex = maxHandlerChainSize
)
// Context 是 Handler 处理消息(事件)的上下文环境. 非并发安全!
type Context struct {
ResponseWriter http.ResponseWriter
Request *http.Request
QueryParams url.Values // 回调请求 URL 的查询参数集合
EncryptType string // 回调请求 URL 的加密方式参数: encrypt_type
MsgSignature string // 回调请求 URL 的消息体签名参数: msg_signature
Signature string // 回调请求 URL 的签名参数: signature
Timestamp int64 // 回调请求 URL 的时间戳参数: timestamp
Nonce string // 回调请求 URL 的随机数参数: nonce
MsgCiphertext []byte // 消息的密文文本
MsgPlaintext []byte // 消息的明文文本, xml格式
MixedMsg *MixedMsg // 消息
Token string // 当前消息所属公众号的 Token
AESKey []byte // 当前消息加密所用的 aes-key, read-only!!!
Random []byte // 当前消息加密所用的 random, 16-bytes
AppId string // 当前消息加密所用的 AppId
handlers HandlerChain
handlerIndex int
kvs map[string]interface{}
}
// IsAborted 返回 true 如果 Context.Abort() 被调用了, 否则返回 false.
func (ctx *Context) IsAborted() bool {
return ctx.handlerIndex >= abortHandlerIndex
}
// Abort 阻止系统调用当前 handler 后续的 handlers, 即当前的 handler 处理完毕就返回, 一般在 middleware 中调用.
func (ctx *Context) Abort() {
ctx.handlerIndex = abortHandlerIndex
}
// Next 中断当前 handler 程序逻辑执行其后续的 handlers, 一般在 middleware 中调用.
func (ctx *Context) Next() {
for {
ctx.handlerIndex++
if ctx.handlerIndex >= len(ctx.handlers) {
ctx.handlerIndex--
break
}
ctx.handlers[ctx.handlerIndex].ServeMsg(ctx)
}
}
// SetHandlers 设置 handlers 给 Context.Next() 调用, 务必在 Context.Next() 调用之前设置, 否则会 panic.
// NOTE: 此方法一般用不到, 除非你自己实现一个 Handler 给 Server 使用, 参考 ServeMux.
func (ctx *Context) SetHandlers(handlers HandlerChain) {
if len(handlers) > maxHandlerChainSize {
panic("too many handlers")
}
for _, h := range handlers {
if h == nil {
panic("handler can not be nil")
}
}
if ctx.handlerIndex != initHandlerIndex {
panic("can't set handlers after Context.Next() called")
}
ctx.handlers = handlers
}
// Context:kvs =========================================================================================================
// Set 存储 key-value pair 到 Context 中.
func (ctx *Context) Set(key string, value interface{}) {
if ctx.kvs == nil {
ctx.kvs = make(map[string]interface{})
}
ctx.kvs[key] = value
}
// Get 返回 Context 中 key 对应的 value, 如果 key 存在的返回 (value, true), 否则返回 (nil, false).
func (ctx *Context) Get(key string) (value interface{}, exists bool) {
value, exists = ctx.kvs[key]
return
}
// MustGet 返回 Context 中 key 对应的 value, 如果 key 不存在则会 panic.
func (ctx *Context) MustGet(key string) interface{} {
if value, exists := ctx.Get(key); exists {
return value
}
panic(`[kvs] key "` + key + `" does not exist`)
}
// Context:response ====================================================================================================
// RawResponse 表示没有消息回复给微信服务器.
func (ctx *Context) NoneResponse() (err error) {
_, err = ctx.ResponseWriter.Write(successResponseBytes)
return
}
// RawResponse 回复明文消息给微信服务器.
// msg: 经过 encoding/xml.Marshal 得到的结果符合微信消息格式的任何数据结构
func (ctx *Context) RawResponse(msg interface{}) (err error) {
return callback.XmlEncodeResponseMessage(ctx.ResponseWriter, msg)
}
// stringWriter is the interface that wraps the WriteString method.
type stringWriter interface {
WriteString(s string) (n int, err error)
}
// AESResponse 回复aes加密的消息给微信服务器.
// msg: 经过 encoding/xml.Marshal 得到的结果符合微信消息格式的任何数据结构
// timestamp: 时间戳, 如果为 0 则默认使用 Context.Timestamp
// nonce: 随机数, 如果为 "" 则默认使用 Context.Nonce
// random: 16字节的随机字符串, 如果为 nil 则默认使用 Context.Random
func (ctx *Context) AESResponse(msg interface{}, timestamp int64, nonce string, random []byte) (err error) {
if timestamp == 0 {
timestamp = ctx.Timestamp
}
if nonce == "" {
nonce = ctx.Nonce
}
if len(random) == 0 {
random = ctx.Random
}
msgPlaintext, err := callback.XmlMarshalResponseMessage(msg)
if err != nil {
return
}
encryptedMsg := util.AESEncryptMsg(random, msgPlaintext, ctx.AppId, ctx.AESKey)
base64EncryptedMsg := base64.StdEncoding.EncodeToString(encryptedMsg)
timestampString := strconv.FormatInt(timestamp, 10)
msgSignature := util.MsgSign(ctx.Token, timestampString, nonce, base64EncryptedMsg)
if sw, ok := ctx.ResponseWriter.(stringWriter); ok {
if _, err = sw.WriteString("<xml><Encrypt>"); err != nil {
return
}
if _, err = sw.WriteString(base64EncryptedMsg); err != nil {
return
}
if _, err = sw.WriteString("</Encrypt><MsgSignature>"); err != nil {
return
}
if _, err = sw.WriteString(msgSignature); err != nil {
return
}
if _, err = sw.WriteString("</MsgSignature><TimeStamp>"); err != nil {
return
}
if _, err = sw.WriteString(timestampString); err != nil {
return
}
if _, err = sw.WriteString("</TimeStamp><Nonce>"); err != nil {
return
}
if err = xml.EscapeText(ctx.ResponseWriter, []byte(nonce)); err != nil {
return
}
_, err = sw.WriteString("</Nonce></xml>")
return
} else {
bufw := bufio.NewWriterSize(ctx.ResponseWriter, 256)
if _, err = bufw.WriteString("<xml><Encrypt>"); err != nil {
return
}
if _, err = bufw.WriteString(base64EncryptedMsg); err != nil {
return
}
if _, err = bufw.WriteString("</Encrypt><MsgSignature>"); err != nil {
return
}
if _, err = bufw.WriteString(msgSignature); err != nil {
return
}
if _, err = bufw.WriteString("</MsgSignature><TimeStamp>"); err != nil {
return
}
if _, err = bufw.WriteString(timestampString); err != nil {
return
}
if _, err = bufw.WriteString("</TimeStamp><Nonce>"); err != nil {
return
}
if err = xml.EscapeText(bufw, []byte(nonce)); err != nil {
return
}
if _, err = bufw.WriteString("</Nonce></xml>"); err != nil {
return
}
return bufw.Flush()
}
}