Repository navigation
Expand file tree
/
Copy pathdecode.go
More file actions
135 lines (125 loc) · 5.18 KB
/
Copy pathdecode.go
File metadata and controls
135 lines (125 loc) · 5.18 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
package edge
import (
"encoding/json/jsontext"
"encoding/json/v2"
"errors"
"github.com/aws/aws-lambda-go/events"
)
// decodedEvent contains exactly one request on success.
// Authorizer JSON belongs to this value, independently of the input buffer;
// it is not decoded into the SDK's string/float64 claim representations.
// No identity is established here.
type decodedEvent struct {
version string
v1 *events.APIGatewayProxyRequest
v2 *events.APIGatewayV2HTTPRequest
authorizer jsontext.Value
}
func decodeEvent(payload []byte) (decodedEvent, error) {
// This small first pass validates all JSON syntax, including unknown and opaque fields, and retains presence where SDK scalar zero values cannot.
var envelope *struct {
Version jsontext.Value `json:"version"`
IsBase64Encoded jsontext.Value `json:"isBase64Encoded"`
}
if err := json.Unmarshal(payload, &envelope); err != nil {
// JSON errors can contain object names and input values.
// Do not expose them, even through an unwrap chain, at this credential-bearing boundary.
return decodedEvent{}, invocationError(OperationDecode, ErrInvalidEvent, "invalid event JSON")
}
if envelope == nil {
return decodedEvent{}, invocationError(OperationDecode, ErrInvalidEvent, "event must be a JSON object")
}
var version string
if len(envelope.Version) != 0 {
if err := json.Unmarshal(envelope.Version, &version); err != nil {
return decodedEvent{}, invocationError(OperationDecode, ErrInvalidEvent, "invalid payload version")
}
if version != "1.0" && version != "2.0" {
return decodedEvent{}, invocationError(OperationDecode, ErrUnsupportedEvent, "unsupported payload version")
}
}
if len(envelope.IsBase64Encoded) != 0 && envelope.IsBase64Encoded.Kind() != 't' && envelope.IsBase64Encoded.Kind() != 'f' {
return decodedEvent{}, invocationError(OperationDecode, ErrInvalidEvent, "isBase64Encoded must be a boolean")
}
if version == "2.0" {
var wire struct {
events.APIGatewayV2HTTPRequest
RequestContext struct {
events.APIGatewayV2HTTPRequestContext
Authorizer jsontext.Value `json:"authorizer"`
} `json:"requestContext"`
}
if err := json.Unmarshal(payload, &wire, eventStringOptions); err != nil {
return decodedEvent{}, invocationError(OperationDecode, ErrInvalidEvent, "invalid v2 event fields")
}
event := wire.APIGatewayV2HTTPRequest
event.RequestContext = wire.RequestContext.APIGatewayV2HTTPRequestContext
if err := validateV2(event); err != nil {
return decodedEvent{}, err
}
return decodedEvent{version: version, v2: &event, authorizer: wire.RequestContext.Authorizer}, nil
}
var wire struct {
events.APIGatewayProxyRequest
RequestContext struct {
events.APIGatewayProxyRequestContext
Authorizer jsontext.Value `json:"authorizer"`
} `json:"requestContext"`
}
if err := json.Unmarshal(payload, &wire, eventStringOptions); err != nil {
return decodedEvent{}, invocationError(OperationDecode, ErrInvalidEvent, "invalid v1 event fields")
}
event := wire.APIGatewayProxyRequest
event.RequestContext = wire.RequestContext.APIGatewayProxyRequestContext
if err := validateV1(event); err != nil {
return decodedEvent{}, err
}
return decodedEvent{version: version, v1: &event, authorizer: wire.RequestContext.Authorizer}, nil
}
func validateV1(event events.APIGatewayProxyRequest) error {
if event.RequestContext.APIID == "" {
return invocationError(OperationValidate, ErrInvalidEvent, "missing requestContext.apiId")
}
if event.HTTPMethod == "" {
return invocationError(OperationValidate, ErrInvalidEvent, "missing httpMethod")
}
if event.Path == "" {
return invocationError(OperationValidate, ErrInvalidEvent, "missing path")
}
return nil
}
func validateV2(event events.APIGatewayV2HTTPRequest) error {
if event.Version != "" && event.Version != "2.0" {
return invocationError(OperationValidate, ErrInvalidEvent, "conflicting v2 payload version")
}
if event.RequestContext.APIID == "" {
return invocationError(OperationValidate, ErrInvalidEvent, "missing requestContext.apiId")
}
if event.RequestContext.HTTP.Method == "" {
return invocationError(OperationValidate, ErrInvalidEvent, "missing requestContext.http.method")
}
if event.RawPath == "" {
return invocationError(OperationValidate, ErrInvalidEvent, "missing rawPath")
}
return nil
}
// Optional collections may be null, but their string elements must be strings.
// Scope this rule to collections: AWS also sends null optional scalar metadata and body fields.
// Options and unmarshaler lists are immutable after creation.
var eventStringOptions = json.WithUnmarshalers(json.JoinUnmarshalers(
json.UnmarshalFunc(func(data []byte, out *map[string]string) error {
return json.Unmarshal(data, out, nonNullStringOptions)
}),
json.UnmarshalFunc(func(data []byte, out *map[string][]string) error {
return json.Unmarshal(data, out, nonNullStringOptions)
}),
json.UnmarshalFunc(func(data []byte, out *[]string) error {
return json.Unmarshal(data, out, nonNullStringOptions)
}),
))
var nonNullStringOptions = json.WithUnmarshalers(json.UnmarshalFunc(func(data []byte, out *string) error {
if jsontext.Value(data).Kind() != '"' {
return errors.New("expected a JSON string")
}
return json.Unmarshal(data, out)
}))