-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy pathmux_lambda_wrapper.go
More file actions
187 lines (159 loc) · 5.46 KB
/
mux_lambda_wrapper.go
File metadata and controls
187 lines (159 loc) · 5.46 KB
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
// Copyright 2021-2024 Nokia
// Licensed under the BSD 3-Clause License.
// SPDX-License-Identifier: BSD-3-Clause
package restful
import (
"context"
"net/http"
"reflect"
"github.com/go-playground/validator/v10"
"github.com/nokia/restful/lambda"
)
// LambdaMaxBytesToParse defines the maximum length of the request content allowed to be parsed.
// If zero then no limits imposed.
var LambdaMaxBytesToParse = 0
// LambdaSanitizeJSON defines whether to sanitize JSON of Lambda return or SendResp.
// See SanitizeJSONString for details.
// Deprecated.
var LambdaSanitizeJSON = false
// LambdaValidator tells if incoming request is to be validated.
// Validation is done by https://github.com/go-playground/validator.
// See its documentation for details.
var LambdaValidator = true
// LambdaValidationErrorStatus is the HTTP status code to return on message validation error.
// Default is 422 Unprocessable Entity, similarly to Python FastAPI/Pydantic.
// If you prefer 400 Bad Request, then change this.
var LambdaValidationErrorStatus = http.StatusUnprocessableEntity
// Validate is a singleton global object that performs Lambda server request validation
// of structs with fields with `validate` tagging.
// Validation is done by https://github.com/go-playground/validator.
// You can rely on the default value, unless you use custom validators.
var Validate *validator.Validate = validator.New(validator.WithRequiredStructEnabled())
// ValidateErrConverter is a function variable that allows customization of error
// returned by validator. You can provide your own function here.
// This can be used if some standardized formatting is desired.
var ValidateErrConverter func(err error) error
// ReadErrConverter is a function variable similar to ValidateErrConverter
// that allows customization of errors returned when the request body is read and Unmarhsaled
var ReadErrConverter func(err error) error
func lambdaHandleRes0(l *lambda.Lambda) (err error) {
if l != nil && l.Status > 0 {
err = NewError(nil, l.Status)
}
return
}
func lambdaHandleRes1(l *lambda.Lambda, res reflect.Value) (any, error) {
if err, ok := res.Interface().(error); ok {
return nil, err
}
if l != nil && l.Status > 0 {
return res.Interface(), NewError(nil, l.Status)
}
return res.Interface(), nil
}
func lambdaGetStatus(l *lambda.Lambda, res reflect.Value) error {
if err, ok := res.Interface().(error); ok {
return err
}
if l != nil && l.Status > 0 {
return NewError(nil, l.Status)
}
return nil
}
func lambdaHandleRes2(l *lambda.Lambda, res []reflect.Value) (any, error) {
err := lambdaGetStatus(l, res[1])
if res[0].Kind() == reflect.Ptr {
if res[0].IsNil() {
return nil, err
}
res[0] = res[0].Elem()
}
return res[0].Interface(), err
}
func lambdaHandleRes(w http.ResponseWriter, r *http.Request, res []reflect.Value) {
var data any
var err error
if len(res) <= 0 {
err = lambdaHandleRes0(L(r.Context()))
} else if len(res) == 1 {
data, err = lambdaHandleRes1(L(r.Context()), res[0])
} else {
data, err = lambdaHandleRes2(L(r.Context()), res)
}
_ = SendResp(w, r, err, data)
}
var contextType = reflect.TypeOf((*context.Context)(nil)).Elem()
func lambdaGetParams(w http.ResponseWriter, r *http.Request, f any) ([]reflect.Value, *http.Request, error) {
t := reflect.TypeOf(f)
params := make([]reflect.Value, t.NumIn())
if t.NumIn() > 0 {
reqDataIdx := 0
// Handle context parameter
if t.In(0).Implements(contextType) {
ctx := NewRequestCtx(w, r)
r = r.WithContext(ctx)
params[0] = reflect.ValueOf(ctx)
reqDataIdx = 1
}
// Handle body parameter
if reqDataIdx < t.NumIn() {
var reqData reflect.Value
var reqDataInterface any
reqDataType := t.In(reqDataIdx)
if reqDataType.Kind() == reflect.Ptr {
reqData = reflect.New(reqDataType.Elem())
reqDataInterface = reqData.Interface()
} else {
reqData = reflect.New(reqDataType).Elem()
reqDataInterface = reqData.Addr().Interface()
}
if err := GetRequestData(r, LambdaMaxBytesToParse, reqDataInterface); err != nil {
if ReadErrConverter != nil {
err = ReadErrConverter(err)
if _, ok := err.(*restError); ok { // no need to wrap
return nil, r, err
}
return nil, r, NewError(err, http.StatusInternalServerError)
}
return nil, r, err
}
if LambdaValidator && reflect.ValueOf(reqDataInterface).Elem().Kind() == reflect.Struct {
if err := Validate.Struct(reqDataInterface); err != nil {
if ValidateErrConverter != nil {
err = ValidateErrConverter(err)
if _, ok := err.(*restError); ok { // no need to wrap
return nil, r, err
}
}
return nil, r, NewError(err, LambdaValidationErrorStatus)
}
}
params[reqDataIdx] = reqData
}
}
return params, r, nil
}
// LambdaWrap wraps a Lambda function and makes it a http.HandlerFunc.
// This function is rarely needed, as restful's Router wraps handler functions automatically.
// You might need it if you want to wrap a standard http.HandlerFunc.
func LambdaWrap(f any) http.HandlerFunc {
if httpHandler, ok := f.(func(w http.ResponseWriter, r *http.Request)); ok {
return httpHandler
}
if httpHandler, ok := f.(http.HandlerFunc); ok {
return httpHandler
}
t := reflect.TypeOf(f)
if t.Kind() != reflect.Func {
panic("function expected")
}
return func(w http.ResponseWriter, r *http.Request) {
params, r, err := lambdaGetParams(w, r, f)
if err != nil {
_ = SendResp(w, r, err, nil)
return
}
res := reflect.ValueOf(f).Call(params)
lambdaHandleRes(w, r, res)
}
}