-
Notifications
You must be signed in to change notification settings - Fork 0
/
client.go
299 lines (257 loc) · 7.12 KB
/
client.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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
package wemeet
import (
"bytes"
"crypto"
"crypto/hmac"
"encoding/base64"
"encoding/hex"
"encoding/json"
"fmt"
"io/ioutil"
"math/rand"
"net"
"net/http"
"net/url"
"reflect"
"strconv"
"strings"
"time"
)
func init() {
rand.Seed(time.Now().UnixNano())
}
// Describe A Request
type MeetingRequestDescriptor struct {
Url string
Method string
Tag string
}
// MeetingRequest interface
type MeetingRequest interface {
getDescriptor() *MeetingRequestDescriptor
fillPlaceholder(args ...interface{}) string
fillDefaultValue()
}
type MeetingResponse interface {
}
type MeetingErrorResponse struct {
StatusCode int `json:"-"`
ErrorInfo *MeetingError `json:"error_info"`
}
type MeetingError struct {
Code int `json:"error_code"`
Message string `json:"message"`
}
func (e MeetingError) Error() string {
return fmt.Sprintf("server error: %s (%d)", e.Message, e.Code)
}
type Pager struct {
TotalCount int `json:"total_count"` // 总数
CurrentSize int `json:"current_size"` // 当前页实际大小
CurrentPage int `json:"current_page"` // 当前页数
PageSize int `json:"page_size"` // 分页大小
}
type Meeting struct {
SecretKey string
SecretID string
AppID string
SdkID string
Version string // 软件版本,用于调试
Registered int // 企业用户管理,最好开,否则主持人的功能用不了
}
// RequestBody Descriptor
type Request struct {
Method string
URL *url.URL
Secret string
Body string
Key string `json:"X-TC-Key"`
Timestamp int64 `json:"X-TC-Timestamp"`
Nonce int `json:"X-TC-Nonce"`
Signature string `json:"X-TC-Signature"`
AppID string `json:"AppId"`
SdkID string `json:"SdkId"`
Version string `json:"X-TC-Version"`
Registered int `json:"X-TC-Registered"`
ContentType string `json:"Content-Type"`
ContentLength string `json:"Content-Length"`
}
//var proxyUrl, _ = url.Parse("http://127.0.0.1:18080")
var client = &http.Client{
Transport: &http.Transport{
//Proxy: http.ProxyURL(proxyUrl),
Proxy: http.ProxyFromEnvironment,
DialContext: (&net.Dialer{
Timeout: 30 * time.Second,
KeepAlive: 30 * time.Second,
DualStack: true,
}).DialContext,
ForceAttemptHTTP2: false,
MaxIdleConns: 100,
IdleConnTimeout: 90 * time.Second,
TLSHandshakeTimeout: 10 * time.Second,
ExpectContinueTimeout: 1 * time.Second,
},
}
func GetHttpClient() *http.Client {
return client
}
func newMeetingRequest(method, path, body string, meeting Meeting) *Request {
req := new(Request)
req.ContentType = "application/json"
req.Method = method
req.URL, _ = url.Parse(path)
req.Secret = meeting.SecretKey
req.Body = body
req.Timestamp = time.Now().Unix()
req.Key = meeting.SecretID
req.Nonce = rand.Intn(10000) + 10000
req.Version = meeting.Version
req.AppID = meeting.AppID
req.Registered = meeting.Registered
req.SdkID = meeting.SdkID
return req
}
func NewRequest(method, url, body string, meeting Meeting) (*http.Request, error) {
method = strings.ToUpper(method)
req, err := http.NewRequest(method, url, strings.NewReader(body))
if err != nil {
return nil, err
}
mReq := newMeetingRequest(method, url, body, meeting)
fillSignature(mReq)
fillHeader(mReq, &req.Header)
return req, nil
}
func serializeHeader(request *Request) string {
var buf bytes.Buffer
buf.WriteString("X-TC-Key=" + request.Key +
"&" + "X-TC-Nonce=" + strconv.Itoa(request.Nonce) +
"&" + "X-TC-Timestamp=" + strconv.Itoa(int(request.Timestamp)))
return buf.String()
}
func fillHeader(req *Request, header *http.Header) {
callback := func(n, v string) {
//fmt.Printf("%s:%s\n", n, v)
(*header)[n] = []string{v}
}
fillFields(req, callback)
}
func fillSignature(req *Request) {
ques := ""
if len(req.URL.RawQuery) > 0 {
ques = "?"
}
stringToSign := req.Method + "\n" + serializeHeader(req) + "\n" + req.URL.Path + ques + req.URL.RawQuery + "\n" + req.Body
hm := hmac.New(crypto.SHA256.New, []byte(req.Secret))
hm.Write([]byte(stringToSign))
result := hm.Sum(nil)
// debug stub
//log.Println(stringToSign)
req.Signature = base64.StdEncoding.EncodeToString([]byte(hex.EncodeToString(result)))
}
func fillFields(req *Request, callback func(name, value string)) {
ref := reflect.ValueOf(*req)
typ := reflect.TypeOf(*req)
numRef := ref.NumField()
for i := 0; i < numRef; i++ {
field := ref.Field(i)
fieldType := typ.Field(i)
if fieldType.Tag.Get("json") != "" && !field.IsZero() {
switch field.Kind() {
case reflect.String:
callback(fieldType.Tag.Get("json"), field.String())
case reflect.Int64:
callback(fieldType.Tag.Get("json"), strconv.Itoa(int(field.Int())))
case reflect.Int:
callback(fieldType.Tag.Get("json"), strconv.Itoa(int(field.Int())))
}
}
}
}
func (meeting Meeting) Do(req MeetingRequest) (MeetingResponse, error) {
descriptor := req.getDescriptor()
_, method := descriptor.Url, descriptor.Method
req.fillDefaultValue()
urlAppendix := bytes.NewBufferString("")
urlAppendixFlag := false
reqBody := ""
typ := reflect.TypeOf(req)
val := reflect.ValueOf(req)
if val.Kind().String() == "ptr" {
val = val.Elem()
typ = typ.Elem()
}
params := make([]interface{}, 0, 3)
queries := NewQueryValues()
// inject params and queries
for i := 0; i < typ.NumField(); i++ {
fTyp := typ.Field(i)
fVal := val.Field(i)
if !fVal.IsZero() && fTyp.Tag.Get("query") != "" {
urlAppendixFlag = true
switch fVal.Kind() {
case reflect.String:
queries.Add(fTyp.Tag.Get("query"), fVal.String())
case reflect.Int, reflect.Int64, reflect.Int32:
queries.Add(fTyp.Tag.Get("query"), strconv.Itoa(int(fVal.Int())))
case reflect.Bool:
queries.Add(fTyp.Tag.Get("query"), strconv.FormatBool(fVal.Bool()))
default:
// not formatted. print type
queries.Add(fTyp.Tag.Get("query"), fVal.String())
}
} // if
// 按照struct的顺序来填充数据
if !fVal.IsZero() && fTyp.Tag.Get("param") != "" {
switch fVal.Kind() {
case reflect.String:
params = append(params, fVal.String())
case reflect.Int, reflect.Int64, reflect.Int32:
params = append(params, strconv.Itoa(int(fVal.Int())))
case reflect.Bool:
params = append(params, strconv.FormatBool(fVal.Bool()))
}
} // if
} // for
if urlAppendixFlag {
urlAppendix.WriteString("?" + queries.Encode())
}
switch method {
case "POST", "PUT":
body, err := json.Marshal(req)
if err != nil {
// log.Println(err)
return nil, err
}
reqBody = string(body)
}
hReq, err := NewRequest(method, ApiHost+req.fillPlaceholder(params...)+urlAppendix.String(), reqBody, meeting)
if err != nil {
//log.Println(err)
return nil, err
}
resp, err := client.Do(hReq)
if err != nil {
//log.Println(err)
return nil, err
} else {
res, _ := ioutil.ReadAll(resp.Body)
if resp.StatusCode == 200 {
response, err := meeting.handleResponse(res, descriptor)
if err != nil {
return nil, err
} else {
return response, nil
}
} else {
var errorResponse MeetingErrorResponse
err := json.Unmarshal(res, &errorResponse)
if err != nil {
return nil, err
} else {
return nil, *(errorResponse.ErrorInfo)
}
}
}
}