-
Notifications
You must be signed in to change notification settings - Fork 0
/
session.go
508 lines (463 loc) · 13.5 KB
/
session.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
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
package webserver
import (
"encoding/json"
"net/http"
"net/textproto"
"runtime"
"strconv"
"github.com/google/uuid"
"github.com/gorilla/mux"
)
var (
defaultRequest *http.Request = &http.Request{}
defaultResponseWriter http.ResponseWriter = &nilResponseWriter{}
)
// Session is the storage for the current HTTP request session, containing information needed for logging, monitoring, etc.
type Session interface {
SessionMeta
SessionHTTP
SessionAttachment
SessionLogging
SessionWebcall
}
// SessionMeta is a subset of Session interface, containing only meta data related methods
type SessionMeta interface {
// GetID returns the ID of this registered session object
GetID() uuid.UUID
// GetName returns the name registered to session object for given session ID
GetName() string
}
// SessionHTTP is a subset of Session interface, containing only HTTP request & response related methods
type SessionHTTP interface {
SessionHTTPRequest
SessionHTTPResponse
}
// SessionHTTPRequest is a subset of Session interface, containing only HTTP request related methods
type SessionHTTPRequest interface {
// GetRequest returns the HTTP request object from session object for given session ID
GetRequest() *http.Request
// GetRequestBody loads HTTP request body associated to session and unmarshals the content JSON to given data template
GetRequestBody(dataTemplate interface{}) error
// GetRequestParameter loads HTTP request parameter associated to session for given name and unmarshals the content to given data template
GetRequestParameter(name string, dataTemplate interface{}) error
// GetRequestQuery loads HTTP request single query string associated to session for given name and unmarshals the content to given data template
GetRequestQuery(name string, index int, dataTemplate interface{}) error
// GetRequestHeader loads HTTP request single header string associated to session for given name and unmarshals the content to given data template
GetRequestHeader(name string, index int, dataTemplate interface{}) error
}
// SessionHTTPResponse is a subset of SessionHTTP interface, containing only HTTP response related methods
type SessionHTTPResponse interface {
// GetResponseWriter returns the HTTP response writer object from session object for given session ID
GetResponseWriter() http.ResponseWriter
}
// SessionAttachment is a subset of Session interface, containing only attachment related methods
type SessionAttachment interface {
// Attach attaches any value object into the given session associated to the session ID
Attach(name string, value interface{}) bool
// Detach detaches any value object from the given session associated to the session ID
Detach(name string) bool
// GetRawAttachment retrieves any value object from the given session associated to the session ID and returns the raw interface (consumer needs to manually cast, but works for struct with private fields)
GetRawAttachment(name string) (interface{}, bool)
// GetAttachment retrieves any value object from the given session associated to the session ID and unmarshals the content to given data template (only works for structs with exported fields)
GetAttachment(name string, dataTemplate interface{}) bool
}
// SessionLogging is a subset of Session interface, containing only logging related methods
type SessionLogging interface {
// LogMethodEnter sends a logging entry of MethodEnter log type for the given session associated to the session ID
LogMethodEnter()
// LogMethodParameter sends a logging entry of MethodParameter log type for the given session associated to the session ID
LogMethodParameter(parameters ...interface{})
// LogMethodLogic sends a logging entry of MethodLogic log type for the given session associated to the session ID
LogMethodLogic(logLevel LogLevel, category string, subcategory string, messageFormat string, parameters ...interface{})
// LogMethodReturn sends a logging entry of MethodReturn log type for the given session associated to the session ID
LogMethodReturn(returns ...interface{})
// LogMethodExit sends a logging entry of MethodExit log type for the given session associated to the session ID
LogMethodExit()
}
// SessionWebcall is a subset of Session interface, containing only webcall related methods
type SessionWebcall interface {
// CreateWebcallRequest generates a webcall request object to the targeted external web service for the given session associated to the session ID
CreateWebcallRequest(method string, url string, payload string, sendClientCert bool) WebRequest
}
type session struct {
id uuid.UUID
name string
request *http.Request
responseWriter http.ResponseWriter
attachment map[string]interface{}
customization Customization
}
// GetID returns the ID of this registered session object
func (session *session) GetID() uuid.UUID {
if session == nil {
return uuid.Nil
}
return session.id
}
// GetName returns the name registered to session object for given session ID
func (session *session) GetName() string {
if session == nil {
return ""
}
return session.name
}
// GetRequest returns the HTTP request object from session object for given session ID
func (session *session) GetRequest() *http.Request {
if session == nil ||
session.request == nil {
return defaultRequest
}
return session.request
}
// GetResponseWriter returns the HTTP response writer object from session object for given session ID
func (session *session) GetResponseWriter() http.ResponseWriter {
if session == nil ||
isInterfaceValueNil(session.responseWriter) {
return defaultResponseWriter
}
return session.responseWriter
}
// GetRequestBody loads HTTP request body associated to session and unmarshals the content JSON to given data template
func (session *session) GetRequestBody(dataTemplate interface{}) error {
if session == nil {
return newAppError(
errorCodeGeneralFailure,
errorMessageSessionNil,
[]error{},
)
}
var httpRequest = session.GetRequest()
var requestBody = getRequestBody(
httpRequest,
)
if requestBody == "" {
return newAppError(
errorCodeBadRequest,
errorMessageRequestBodyEmpty,
[]error{},
)
}
logEndpointRequest(
session,
"Body",
"Content",
requestBody,
)
var unmarshalError = tryUnmarshal(
requestBody,
dataTemplate,
)
if unmarshalError != nil {
logEndpointRequest(
session,
"Body",
"UnmarshalError",
"%+v",
unmarshalError,
)
return newAppError(
errorCodeBadRequest,
errorMessageRequestBodyInvalid,
[]error{unmarshalError},
)
}
return nil
}
// GetRequestParameter loads HTTP request parameter associated to session for given name and unmarshals the content to given data template
func (session *session) GetRequestParameter(name string, dataTemplate interface{}) error {
if session == nil {
return newAppError(
errorCodeGeneralFailure,
errorMessageSessionNil,
[]error{},
)
}
var httpRequest = session.GetRequest()
var parameters = mux.Vars(
httpRequest,
)
var value, found = parameters[name]
if !found {
return newAppError(
errorCodeBadRequest,
errorMessageParameterNotFound,
[]error{},
)
}
logEndpointRequest(
session,
"Parameter",
name,
value,
)
var unmarshalError = tryUnmarshal(
value,
dataTemplate,
)
if unmarshalError != nil {
logEndpointRequest(
session,
"Parameter",
"UnmarshalError",
"%+v",
unmarshalError,
)
return newAppError(
errorCodeBadRequest,
errorMessageParameterInvalid,
[]error{unmarshalError},
)
}
return nil
}
func getAllQueries(session *session, name string) []string {
var httpRequest = session.GetRequest()
if httpRequest.URL == nil {
return nil
}
var queries, found = httpRequest.URL.Query()[name]
if !found {
return nil
}
return queries
}
// GetRequestQuery loads HTTP request single query string associated to session for given name and unmarshals the content to given data template
func (session *session) GetRequestQuery(name string, index int, dataTemplate interface{}) error {
if session == nil {
return newAppError(
errorCodeGeneralFailure,
errorMessageSessionNil,
[]error{},
)
}
var queries = getAllQueries(
session,
name,
)
if len(queries) <= index {
return newAppError(
errorCodeBadRequest,
errorMessageQueryNotFound,
[]error{},
)
}
var value = queries[index]
logEndpointRequest(
session,
"Query",
name,
value,
)
var unmarshalError = tryUnmarshal(
value,
dataTemplate,
)
if unmarshalError != nil {
logEndpointRequest(
session,
"Query",
"UnmarshalError",
"%+v",
unmarshalError,
)
return newAppError(
errorCodeBadRequest,
errorMessageQueryInvalid,
[]error{unmarshalError},
)
}
return nil
}
func getAllHeaders(session *session, name string) []string {
var httpRequest = session.GetRequest()
var canonicalName = textproto.CanonicalMIMEHeaderKey(name)
var headers, found = httpRequest.Header[canonicalName]
if !found {
return nil
}
return headers
}
// GetRequestHeader loads HTTP request single header string associated to session for given name and unmarshals the content to given data template
func (session *session) GetRequestHeader(name string, index int, dataTemplate interface{}) error {
if session == nil {
return newAppError(
errorCodeGeneralFailure,
errorMessageSessionNil,
[]error{},
)
}
var headers = getAllHeaders(
session,
name,
)
if len(headers) <= index {
return newAppError(
errorCodeBadRequest,
errorMessageHeaderNotFound,
[]error{},
)
}
var value = headers[index]
logEndpointRequest(
session,
"Header",
name,
value,
)
var unmarshalError = tryUnmarshal(
value,
dataTemplate,
)
if unmarshalError != nil {
logEndpointRequest(
session,
"Header",
"UnmarshalError",
"%+v",
unmarshalError,
)
return newAppError(
errorCodeBadRequest,
errorMessageHeaderInvalid,
[]error{unmarshalError},
)
}
return nil
}
// Attach attaches any value object into the given session associated to the session ID
func (session *session) Attach(name string, value interface{}) bool {
if session == nil {
return false
}
if session.attachment == nil {
session.attachment = map[string]interface{}{}
}
session.attachment[name] = value
return true
}
// Detach detaches any value object from the given session associated to the session ID
func (session *session) Detach(name string) bool {
if session == nil {
return false
}
if session.attachment != nil {
delete(session.attachment, name)
}
return true
}
// GetRawAttachment retrieves any value object from the given session associated to the session ID and returns the raw interface (consumer needs to manually cast, but works for struct with private fields)
func (session *session) GetRawAttachment(name string) (interface{}, bool) {
if session == nil {
return nil, false
}
var attachment, found = session.attachment[name]
if !found {
return nil, false
}
return attachment, true
}
// GetAttachment retrieves any value object from the given session associated to the session ID and unmarshals the content to given data template
func (session *session) GetAttachment(name string, dataTemplate interface{}) bool {
if session == nil {
return false
}
var attachment, found = session.GetRawAttachment(name)
if !found {
return false
}
var bytes, marshalError = json.Marshal(attachment)
if marshalError != nil {
return false
}
var unmarshalError = json.Unmarshal(
bytes,
dataTemplate,
)
return unmarshalError == nil
}
func getMethodName() string {
var pc, _, _, ok = runtime.Caller(3)
if !ok {
return "?"
}
var fn = runtime.FuncForPC(pc)
return fn.Name()
}
// LogMethodEnter sends a logging entry of MethodEnter log type for the given session associated to the session ID
func (session *session) LogMethodEnter() {
var methodName = getMethodName()
logMethodEnter(
session,
methodName,
"",
"",
)
}
// LogMethodParameter sends a logging entry of MethodParameter log type for the given session associated to the session ID
func (session *session) LogMethodParameter(parameters ...interface{}) {
var methodName = getMethodName()
for index, parameter := range parameters {
logMethodParameter(
session,
methodName,
strconv.Itoa(index),
"%v",
parameter,
)
}
}
// LogMethodLogic sends a logging entry of MethodLogic log type for the given session associated to the session ID
func (session *session) LogMethodLogic(logLevel LogLevel, category string, subcategory string, messageFormat string, parameters ...interface{}) {
logMethodLogic(
session,
logLevel,
category,
subcategory,
messageFormat,
parameters...,
)
}
// LogMethodReturn sends a logging entry of MethodReturn log type for the given session associated to the session ID
func (session *session) LogMethodReturn(returns ...interface{}) {
var methodName = getMethodName()
for index, returnValue := range returns {
logMethodReturn(
session,
methodName,
strconv.Itoa(index),
"%v",
returnValue,
)
}
}
// LogMethodExit sends a logging entry of MethodExit log type for the given session associated to the session ID
func (session *session) LogMethodExit() {
var methodName = getMethodName()
logMethodExit(
session,
methodName,
"",
"",
)
}
// CreateWebcallRequest generates a webcall request object to the targeted external web service for the given session associated to the session ID
func (session *session) CreateWebcallRequest(
method string,
url string,
payload string,
sendClientCert bool,
) WebRequest {
return &webRequest{
session,
method,
url,
payload,
map[string][]string{},
map[string][]string{},
0,
nil,
sendClientCert,
0,
[]dataReceiver{},
}
}