forked from 0angelic0/aws-lambda-go-api-proxy
/
requestalb.go
158 lines (137 loc) · 5.17 KB
/
requestalb.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
// Package core provides utility methods that help convert proxy events
// into an http.Request and http.ResponseWriter
package core
import (
"bytes"
"context"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"log"
"net/http"
"net/url"
"strings"
"github.com/aws/aws-lambda-go/events"
"github.com/aws/aws-lambda-go/lambdacontext"
)
const (
// ALBTgContextHeader is the custom header key used to store the
// ALB Target Group Request context. To access the Context properties use the
// RequestAccessorALB method of the RequestAccessorALB object.
ALBTgContextHeader = "X-Golambdaproxy-Albtargetgroup-Context"
)
// RequestAccessorALB objects give access to custom ALB Target Group properties
// in the request.
type RequestAccessorALB struct{}
// GetALBTargetGroupRequestContext extracts the ALB Target Group Responce context object from a
// request's custom header.
// Returns a populated events.ALBTargetGroupRequestContext object from
// the request.
func (r *RequestAccessorALB) GetALBTargetGroupRequestContext(req *http.Request) (events.ALBTargetGroupRequestContext, error) {
if req.Header.Get(ALBTgContextHeader) == "" {
return events.ALBTargetGroupRequestContext{}, errors.New("No context header in request")
}
context := events.ALBTargetGroupRequestContext{}
err := json.Unmarshal([]byte(req.Header.Get(ALBTgContextHeader)), &context)
if err != nil {
log.Println("Erorr while unmarshalling context")
log.Println(err)
return events.ALBTargetGroupRequestContext{}, err
}
return context, nil
}
// ProxyEventToHTTPRequest converts an ALB Target Group proxy event into a http.Request object.
// Returns the populated http request with additional two custom headers for ALB Tg Req context.
// To access these properties use the GetALBTargetGroupRequestContext method of the RequestAccessor object.
func (r *RequestAccessorALB) ProxyEventToHTTPRequest(req events.ALBTargetGroupRequest) (*http.Request, error) {
httpRequest, err := r.EventToRequest(req)
if err != nil {
log.Println(err)
return nil, err
}
return addToHeaderALB(httpRequest, req)
}
// EventToRequestWithContext converts an ALB Target Group proxy event and context into an http.Request object.
// Returns the populated http request with lambda context, ALBTargetGroupRequestContext as part of its context.
// Access those using GetRuntimeContextFromContextALB and GetRuntimeContextFromContext functions in this package.
func (r *RequestAccessorALB) EventToRequestWithContext(ctx context.Context, req events.ALBTargetGroupRequest) (*http.Request, error) {
httpRequest, err := r.EventToRequest(req)
if err != nil {
log.Println(err)
return nil, err
}
return addToContextALB(ctx, httpRequest, req), nil
}
// EventToRequest converts an ALB Target group proxy event into an http.Request object.
// Returns the populated request maintaining headers
func (r *RequestAccessorALB) EventToRequest(req events.ALBTargetGroupRequest) (*http.Request, error) {
decodedBody := []byte(req.Body)
if req.IsBase64Encoded {
base64Body, err := base64.StdEncoding.DecodeString(req.Body)
if err != nil {
return nil, err
}
decodedBody = base64Body
}
path := req.Path
if !strings.HasPrefix(path, "/") {
path = "/" + path
}
if len(req.QueryStringParameters) > 0 {
values := url.Values{}
for key, value := range req.QueryStringParameters {
unEscapedKey, _ := url.QueryUnescape(key)
unEscapedValue, _ := url.QueryUnescape(value)
values.Add(unEscapedKey, unEscapedValue)
}
path += "?" + values.Encode()
}
httpRequest, err := http.NewRequest(
strings.ToUpper(req.HTTPMethod),
path,
bytes.NewReader(decodedBody),
)
if err != nil {
fmt.Printf("Could not convert request %s:%s to http.Request\n", req.HTTPMethod, req.Path)
log.Println(err)
return nil, err
}
for headerKey, headerValue := range req.Headers {
for _, val := range strings.Split(headerValue, ",") {
httpRequest.Header.Add(headerKey, strings.Trim(val, " "))
}
}
for headerKey, headerValue := range req.MultiValueHeaders {
for _, arrVal := range headerValue {
for _, val := range strings.Split(arrVal, ",") {
httpRequest.Header.Add(headerKey, strings.Trim(val, " "))
}
}
}
httpRequest.RequestURI = httpRequest.URL.RequestURI()
return httpRequest, nil
}
func addToHeaderALB(req *http.Request, albTgRequest events.ALBTargetGroupRequest) (*http.Request, error) {
albTgContext, err := json.Marshal(albTgRequest.RequestContext)
if err != nil {
log.Println("Could not Marshal ALB Tg context for custom header")
return req, err
}
req.Header.Add(ALBTgContextHeader, string(albTgContext))
return req, nil
}
func addToContextALB(ctx context.Context, req *http.Request, albTgRequest events.ALBTargetGroupRequest) *http.Request {
lc, _ := lambdacontext.FromContext(ctx)
rc := requestContextALB{lambdaContext: lc, gatewayProxyContext: albTgRequest.RequestContext}
ctx = context.WithValue(ctx, ctxKey{}, rc)
return req.WithContext(ctx)
}
func GetRuntimeContextFromContextALB(ctx context.Context) (*lambdacontext.LambdaContext, bool) {
v, ok := ctx.Value(ctxKey{}).(requestContextALB)
return v.lambdaContext, ok
}
type requestContextALB struct {
lambdaContext *lambdacontext.LambdaContext
gatewayProxyContext events.ALBTargetGroupRequestContext
}