From 0057c9d7af4bcb829dc107a28471ad6b2a6db3ba Mon Sep 17 00:00:00 2001 From: Janne Snabb Date: Thu, 7 Feb 2019 20:58:16 +0200 Subject: [PATCH 01/12] Pass context.Context from Lambda runtime to http.Request. Fixes https://github.com/awslabs/aws-lambda-go-api-proxy/issues/27 --- chi/adapter.go | 5 +++-- chi/chilambda_test.go | 3 ++- gin/adapter.go | 5 +++-- gin/ginlambda_test.go | 3 ++- gorillamux/adapter.go | 5 +++-- gorillamux/adapter_test.go | 5 +++-- handlerfunc/adapter.go | 5 +++-- handlerfunc/adapter_test.go | 3 ++- httpadapter/adapter.go | 5 +++-- httpadapter/adapter_test.go | 3 ++- negroni/adapter.go | 5 +++-- negroni/adapter_test.go | 5 +++-- sample/main.go | 5 +++-- 13 files changed, 35 insertions(+), 22 deletions(-) diff --git a/chi/adapter.go b/chi/adapter.go index e886960..44afbff 100644 --- a/chi/adapter.go +++ b/chi/adapter.go @@ -4,6 +4,7 @@ package chiadapter import ( + "context" "net/http" "github.com/aws/aws-lambda-go/events" @@ -30,7 +31,7 @@ func New(chi *chi.Mux) *ChiLambda { // Proxy receives an API Gateway proxy event, transforms it into an http.Request // object, and sends it to the chi.Mux for routing. // It returns a proxy response object gneerated from the http.ResponseWriter. -func (g *ChiLambda) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { +func (g *ChiLambda) Proxy(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { chiRequest, err := g.ProxyEventToHTTPRequest(req) if err != nil { @@ -38,7 +39,7 @@ func (g *ChiLambda) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayP } respWriter := core.NewProxyResponseWriter() - g.chiMux.ServeHTTP(http.ResponseWriter(respWriter), chiRequest) + g.chiMux.ServeHTTP(http.ResponseWriter(respWriter), chiRequest.WithContext(ctx)) proxyResponse, err := respWriter.GetProxyResponse() if err != nil { diff --git a/chi/chilambda_test.go b/chi/chilambda_test.go index 37b6105..7f7080a 100644 --- a/chi/chilambda_test.go +++ b/chi/chilambda_test.go @@ -1,6 +1,7 @@ package chiadapter_test import ( + "context" "log" "net/http" @@ -29,7 +30,7 @@ var _ = Describe("ChiLambda tests", func() { HTTPMethod: "GET", } - resp, err := adapter.Proxy(req) + resp, err := adapter.Proxy(context.Background(), req) Expect(err).To(BeNil()) Expect(resp.StatusCode).To(Equal(200)) diff --git a/gin/adapter.go b/gin/adapter.go index 9372449..7ee2d20 100644 --- a/gin/adapter.go +++ b/gin/adapter.go @@ -4,6 +4,7 @@ package ginadapter import ( + "context" "net/http" "github.com/aws/aws-lambda-go/events" @@ -30,7 +31,7 @@ func New(gin *gin.Engine) *GinLambda { // Proxy receives an API Gateway proxy event, transforms it into an http.Request // object, and sends it to the gin.Engine for routing. // It returns a proxy response object gneerated from the http.ResponseWriter. -func (g *GinLambda) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { +func (g *GinLambda) Proxy(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { ginRequest, err := g.ProxyEventToHTTPRequest(req) if err != nil { @@ -38,7 +39,7 @@ func (g *GinLambda) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayP } respWriter := core.NewProxyResponseWriter() - g.ginEngine.ServeHTTP(http.ResponseWriter(respWriter), ginRequest) + g.ginEngine.ServeHTTP(http.ResponseWriter(respWriter), ginRequest.WithContext(ctx)) proxyResponse, err := respWriter.GetProxyResponse() if err != nil { diff --git a/gin/ginlambda_test.go b/gin/ginlambda_test.go index 34f7064..4091be9 100644 --- a/gin/ginlambda_test.go +++ b/gin/ginlambda_test.go @@ -1,6 +1,7 @@ package ginadapter_test import ( + "context" "log" "github.com/aws/aws-lambda-go/events" @@ -30,7 +31,7 @@ var _ = Describe("GinLambda tests", func() { HTTPMethod: "GET", } - resp, err := adapter.Proxy(req) + resp, err := adapter.Proxy(context.Background(), req) Expect(err).To(BeNil()) Expect(resp.StatusCode).To(Equal(200)) diff --git a/gorillamux/adapter.go b/gorillamux/adapter.go index d5623c6..ebf2cfe 100644 --- a/gorillamux/adapter.go +++ b/gorillamux/adapter.go @@ -1,6 +1,7 @@ package gorillamux import ( + "context" "net/http" "github.com/aws/aws-lambda-go/events" @@ -19,14 +20,14 @@ func New(router *mux.Router) *GorillaMuxAdapter { } } -func (h *GorillaMuxAdapter) Proxy(event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { +func (h *GorillaMuxAdapter) Proxy(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { req, err := h.ProxyEventToHTTPRequest(event) if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) } w := core.NewProxyResponseWriter() - h.router.ServeHTTP(http.ResponseWriter(w), req) + h.router.ServeHTTP(http.ResponseWriter(w), req.WithContext(ctx)) resp, err := w.GetProxyResponse() if err != nil { diff --git a/gorillamux/adapter_test.go b/gorillamux/adapter_test.go index 73a5441..54d9979 100644 --- a/gorillamux/adapter_test.go +++ b/gorillamux/adapter_test.go @@ -1,6 +1,7 @@ package gorillamux_test import ( + "context" "fmt" "net/http" @@ -36,7 +37,7 @@ var _ = Describe("GorillaMuxAdapter tests", func() { HTTPMethod: "GET", } - homePageResp, homePageReqErr := adapter.Proxy(homePageReq) + homePageResp, homePageReqErr := adapter.Proxy(context.Background(), homePageReq) Expect(homePageReqErr).To(BeNil()) Expect(homePageResp.StatusCode).To(Equal(200)) @@ -47,7 +48,7 @@ var _ = Describe("GorillaMuxAdapter tests", func() { HTTPMethod: "GET", } - productsPageResp, productsPageReqErr := adapter.Proxy(productsPageReq) + productsPageResp, productsPageReqErr := adapter.Proxy(context.Background(), productsPageReq) Expect(productsPageReqErr).To(BeNil()) Expect(productsPageResp.StatusCode).To(Equal(200)) diff --git a/handlerfunc/adapter.go b/handlerfunc/adapter.go index f39f598..1770847 100644 --- a/handlerfunc/adapter.go +++ b/handlerfunc/adapter.go @@ -1,6 +1,7 @@ package handlerfunc import ( + "context" "net/http" "github.com/aws/aws-lambda-go/events" @@ -18,14 +19,14 @@ func New(handlerFunc http.HandlerFunc) *HandlerFuncAdapter { } } -func (h *HandlerFuncAdapter) Proxy(event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { +func (h *HandlerFuncAdapter) Proxy(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { req, err := h.ProxyEventToHTTPRequest(event) if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) } w := core.NewProxyResponseWriter() - h.handlerFunc.ServeHTTP(http.ResponseWriter(w), req) + h.handlerFunc.ServeHTTP(http.ResponseWriter(w), req.WithContext(ctx)) resp, err := w.GetProxyResponse() if err != nil { diff --git a/handlerfunc/adapter_test.go b/handlerfunc/adapter_test.go index 29850d5..cadd53b 100644 --- a/handlerfunc/adapter_test.go +++ b/handlerfunc/adapter_test.go @@ -1,6 +1,7 @@ package handlerfunc_test import ( + "context" "fmt" "log" "net/http" @@ -29,7 +30,7 @@ var _ = Describe("HandlerFuncAdapter tests", func() { HTTPMethod: "GET", } - resp, err := adapter.Proxy(req) + resp, err := adapter.Proxy(context.Background(), req) Expect(err).To(BeNil()) Expect(resp.StatusCode).To(Equal(200)) diff --git a/httpadapter/adapter.go b/httpadapter/adapter.go index 0f3032c..2c28912 100644 --- a/httpadapter/adapter.go +++ b/httpadapter/adapter.go @@ -1,6 +1,7 @@ package httpadapter import ( + "context" "net/http" "github.com/aws/aws-lambda-go/events" @@ -18,14 +19,14 @@ func New(handler http.Handler) *HandlerAdapter { } } -func (h *HandlerAdapter) Proxy(event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { +func (h *HandlerAdapter) Proxy(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { req, err := h.ProxyEventToHTTPRequest(event) if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) } w := core.NewProxyResponseWriter() - h.handler.ServeHTTP(http.ResponseWriter(w), req) + h.handler.ServeHTTP(http.ResponseWriter(w), req.WithContext(ctx)) resp, err := w.GetProxyResponse() if err != nil { diff --git a/httpadapter/adapter_test.go b/httpadapter/adapter_test.go index 1e31bfd..a03b52f 100644 --- a/httpadapter/adapter_test.go +++ b/httpadapter/adapter_test.go @@ -1,6 +1,7 @@ package httpadapter_test import ( + "context" "fmt" "log" "net/http" @@ -36,7 +37,7 @@ var _ = Describe("HTTPAdapter tests", func() { HTTPMethod: "GET", } - resp, err := adapter.Proxy(req) + resp, err := adapter.Proxy(context.Background(), req) Expect(err).To(BeNil()) Expect(resp.StatusCode).To(Equal(200)) diff --git a/negroni/adapter.go b/negroni/adapter.go index 74e1ce9..6ca06e3 100644 --- a/negroni/adapter.go +++ b/negroni/adapter.go @@ -1,6 +1,7 @@ package negroniadapter import ( + "context" "net/http" "github.com/aws/aws-lambda-go/events" @@ -19,14 +20,14 @@ func New(n *negroni.Negroni) *NegroniAdapter { } } -func (h *NegroniAdapter) Proxy(event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { +func (h *NegroniAdapter) Proxy(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { req, err := h.ProxyEventToHTTPRequest(event) if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) } w := core.NewProxyResponseWriter() - h.n.ServeHTTP(http.ResponseWriter(w), req) + h.n.ServeHTTP(http.ResponseWriter(w), req.WithContext(ctx)) resp, err := w.GetProxyResponse() if err != nil { diff --git a/negroni/adapter_test.go b/negroni/adapter_test.go index 22480b6..d689427 100644 --- a/negroni/adapter_test.go +++ b/negroni/adapter_test.go @@ -1,6 +1,7 @@ package negroniadapter_test import ( + "context" "fmt" "log" "net/http" @@ -42,7 +43,7 @@ var _ = Describe("NegroniAdapter tests", func() { HTTPMethod: "GET", } - homePageResp, homePageReqErr := adapter.Proxy(homePageReq) + homePageResp, homePageReqErr := adapter.Proxy(context.Background(), homePageReq) Expect(homePageReqErr).To(BeNil()) Expect(homePageResp.StatusCode).To(Equal(200)) @@ -53,7 +54,7 @@ var _ = Describe("NegroniAdapter tests", func() { HTTPMethod: "GET", } - productsPageResp, productsPageReqErr := adapter.Proxy(productsPageReq) + productsPageResp, productsPageReqErr := adapter.Proxy(context.Background(), productsPageReq) Expect(productsPageReqErr).To(BeNil()) Expect(productsPageResp.StatusCode).To(Equal(200)) diff --git a/sample/main.go b/sample/main.go index 747dde0..4b20f98 100644 --- a/sample/main.go +++ b/sample/main.go @@ -1,6 +1,7 @@ package main import ( + "context" "log" "net/http" "strconv" @@ -15,7 +16,7 @@ var ginLambda *ginadapter.GinLambda // Handler is the main entry point for Lambda. Receives a proxy request and // returns a proxy response -func Handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { +func Handler(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { if ginLambda == nil { // stdout and stderr are sent to AWS CloudWatch Logs log.Printf("Gin cold start") @@ -27,7 +28,7 @@ func Handler(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, ginLambda = ginadapter.New(r) } - return ginLambda.Proxy(req) + return ginLambda.Proxy(ctx, req) } func main() { From b08eece063489503c31216690ebd77b5b4ca633d Mon Sep 17 00:00:00 2001 From: Nikolay Sarychev Date: Thu, 25 Apr 2019 09:26:38 -0600 Subject: [PATCH 02/12] Move API GW context to context.Context --- core/request.go | 15 +++++++++++++++ core/request_test.go | 5 +++++ 2 files changed, 20 insertions(+) diff --git a/core/request.go b/core/request.go index d6e4c75..b303c12 100644 --- a/core/request.go +++ b/core/request.go @@ -4,6 +4,7 @@ package core import ( "bytes" + "context" "encoding/base64" "encoding/json" "errors" @@ -174,6 +175,20 @@ func (r *RequestAccessor) ProxyEventToHTTPRequest(req events.APIGatewayProxyRequ } httpRequest.Header.Add(APIGwContextHeader, string(apiGwContext)) httpRequest.Header.Add(APIGwStageVarsHeader, string(stageVars)) + httpRequest = httpRequest.WithContext(toContext(httpRequest.Context(), req.RequestContext)) return httpRequest, nil } + +func toContext(parent context.Context, gwContext events.APIGatewayProxyRequestContext) context.Context { + parent = context.WithValue(parent, ctxKey{}, gwContext) + return parent +} + +// GetAPIGatewayProxyRequestContext retrieve APIGatewayProxyRequestContext from context.Context +func (r *RequestAccessor) GetAPIGatewayProxyRequestContext(req *http.Request) (events.APIGatewayProxyRequestContext, bool) { + v, ok := req.Context().Value(ctxKey{}).(events.APIGatewayProxyRequestContext) + return v, ok +} + +type ctxKey struct{} diff --git a/core/request_test.go b/core/request_test.go index 3c3d020..cd7fa5f 100644 --- a/core/request_test.go +++ b/core/request_test.go @@ -132,6 +132,11 @@ var _ = Describe("RequestAccessor tests", func() { Expect("x").To(Equal(context.AccountID)) Expect("x").To(Equal(context.RequestID)) Expect("x").To(Equal(context.APIID)) + proxyContext, ok := accessor.GetAPIGatewayProxyRequestContext(httpReq) + Expect(ok).To(BeTrue()) + Expect("x").To(Equal(proxyContext.APIID)) + Expect("x").To(Equal(proxyContext.RequestID)) + Expect("x").To(Equal(proxyContext.APIID)) Expect("prod").To(Equal(context.Stage)) }) From 19cfaa41e37c482840240d9019aff152f5403e75 Mon Sep 17 00:00:00 2001 From: Nikolay Sarychev Date: Thu, 25 Apr 2019 09:41:44 -0600 Subject: [PATCH 03/12] Remove api GW context header --- core/request.go | 42 +++++++++++------------------------------- core/request_test.go | 11 ++++------- 2 files changed, 15 insertions(+), 38 deletions(-) diff --git a/core/request.go b/core/request.go index b303c12..849d47d 100644 --- a/core/request.go +++ b/core/request.go @@ -30,7 +30,7 @@ const DefaultServerAddress = "https://aws-serverless-go-api.com" // APIGwContextHeader is the custom header key used to store the // API Gateway context. To access the Context properties use the // GetAPIGatewayContext method of the RequestAccessor object. -const APIGwContextHeader = "X-GoLambdaProxy-ApiGw-Context" +//const APIGwContextHeader = "X-GoLambdaProxy-ApiGw-Context" // APIGwStageVarsHeader is the custom header key used to store the // API Gateway stage variables. To access the stage variable values @@ -44,21 +44,15 @@ type RequestAccessor struct { } // GetAPIGatewayContext extracts the API Gateway context object from a -// request's custom header. +// request's context. // Returns a populated events.APIGatewayProxyRequestContext object from -// the request. +// the request context. func (r *RequestAccessor) GetAPIGatewayContext(req *http.Request) (events.APIGatewayProxyRequestContext, error) { - if req.Header.Get(APIGwContextHeader) == "" { - return events.APIGatewayProxyRequestContext{}, errors.New("No context header in request") - } - context := events.APIGatewayProxyRequestContext{} - err := json.Unmarshal([]byte(req.Header.Get(APIGwContextHeader)), &context) - if err != nil { - log.Println("Erorr while unmarshalling context") - log.Println(err) - return events.APIGatewayProxyRequestContext{}, err + v, ok := req.Context().Value(apiGatewayProxyRequestContextKey{}).(events.APIGatewayProxyRequestContext) + if !ok { + return events.APIGatewayProxyRequestContext{}, errors.New("No APIGatewayProxyRequestContext found in request") } - return context, nil + return v, nil } // GetAPIGatewayStageVars extracts the API Gateway stage variables from a @@ -162,33 +156,19 @@ func (r *RequestAccessor) ProxyEventToHTTPRequest(req events.APIGatewayProxyRequ for h := range req.Headers { httpRequest.Header.Add(h, req.Headers[h]) } - - apiGwContext, err := json.Marshal(req.RequestContext) - if err != nil { - log.Println("Could not Marshal API GW context for custom header") - return nil, err - } stageVars, err := json.Marshal(req.StageVariables) if err != nil { log.Println("Could not marshal stage variables for custom header") return nil, err } - httpRequest.Header.Add(APIGwContextHeader, string(apiGwContext)) httpRequest.Header.Add(APIGwStageVarsHeader, string(stageVars)) - httpRequest = httpRequest.WithContext(toContext(httpRequest.Context(), req.RequestContext)) + httpRequest = httpRequest.WithContext(toContext(httpRequest, req.RequestContext)) return httpRequest, nil } -func toContext(parent context.Context, gwContext events.APIGatewayProxyRequestContext) context.Context { - parent = context.WithValue(parent, ctxKey{}, gwContext) - return parent -} - -// GetAPIGatewayProxyRequestContext retrieve APIGatewayProxyRequestContext from context.Context -func (r *RequestAccessor) GetAPIGatewayProxyRequestContext(req *http.Request) (events.APIGatewayProxyRequestContext, bool) { - v, ok := req.Context().Value(ctxKey{}).(events.APIGatewayProxyRequestContext) - return v, ok +func toContext(req *http.Request, gwContext events.APIGatewayProxyRequestContext) context.Context { + return context.WithValue(req.Context(), apiGatewayProxyRequestContextKey{}, gwContext) } -type ctxKey struct{} +type apiGatewayProxyRequestContextKey struct{} diff --git a/core/request_test.go b/core/request_test.go index cd7fa5f..2907db7 100644 --- a/core/request_test.go +++ b/core/request_test.go @@ -94,8 +94,10 @@ var _ = Describe("RequestAccessor tests", func() { It("Populates context header correctly", func() { httpReq, err := accessor.ProxyEventToHTTPRequest(contextRequest) Expect(err).To(BeNil()) - Expect(2).To(Equal(len(httpReq.Header))) - Expect(httpReq.Header.Get(core.APIGwContextHeader)).ToNot(BeNil()) + Expect(1).To(Equal(len(httpReq.Header))) + context, err := accessor.GetAPIGatewayContext(httpReq) + Expect(err).To(BeNil()) + Expect(context).ToNot(BeNil()) }) }) @@ -132,11 +134,6 @@ var _ = Describe("RequestAccessor tests", func() { Expect("x").To(Equal(context.AccountID)) Expect("x").To(Equal(context.RequestID)) Expect("x").To(Equal(context.APIID)) - proxyContext, ok := accessor.GetAPIGatewayProxyRequestContext(httpReq) - Expect(ok).To(BeTrue()) - Expect("x").To(Equal(proxyContext.APIID)) - Expect("x").To(Equal(proxyContext.RequestID)) - Expect("x").To(Equal(proxyContext.APIID)) Expect("prod").To(Equal(context.Stage)) }) From 77e0525d8c919d591bfe3dcffc1c07e4670a08bf Mon Sep 17 00:00:00 2001 From: Nikolay Sarychev Date: Thu, 25 Apr 2019 12:30:25 -0600 Subject: [PATCH 04/12] Pass APIGatewayContext in Request.Context --- core/request.go | 37 ++++++++++++++++++++++++++++--------- core/request_test.go | 11 +++++++---- 2 files changed, 35 insertions(+), 13 deletions(-) diff --git a/core/request.go b/core/request.go index 849d47d..cfb68c1 100644 --- a/core/request.go +++ b/core/request.go @@ -30,7 +30,7 @@ const DefaultServerAddress = "https://aws-serverless-go-api.com" // APIGwContextHeader is the custom header key used to store the // API Gateway context. To access the Context properties use the // GetAPIGatewayContext method of the RequestAccessor object. -//const APIGwContextHeader = "X-GoLambdaProxy-ApiGw-Context" +const APIGwContextHeader = "X-GoLambdaProxy-ApiGw-Context" // APIGwStageVarsHeader is the custom header key used to store the // API Gateway stage variables. To access the stage variable values @@ -44,15 +44,21 @@ type RequestAccessor struct { } // GetAPIGatewayContext extracts the API Gateway context object from a -// request's context. +// request's custom header. // Returns a populated events.APIGatewayProxyRequestContext object from -// the request context. +// the request. func (r *RequestAccessor) GetAPIGatewayContext(req *http.Request) (events.APIGatewayProxyRequestContext, error) { - v, ok := req.Context().Value(apiGatewayProxyRequestContextKey{}).(events.APIGatewayProxyRequestContext) - if !ok { - return events.APIGatewayProxyRequestContext{}, errors.New("No APIGatewayProxyRequestContext found in request") + if req.Header.Get(APIGwContextHeader) == "" { + return events.APIGatewayProxyRequestContext{}, errors.New("No context header in request") + } + context := events.APIGatewayProxyRequestContext{} + err := json.Unmarshal([]byte(req.Header.Get(APIGwContextHeader)), &context) + if err != nil { + log.Println("Erorr while unmarshalling context") + log.Println(err) + return events.APIGatewayProxyRequestContext{}, err } - return v, nil + return context, nil } // GetAPIGatewayStageVars extracts the API Gateway stage variables from a @@ -156,11 +162,18 @@ func (r *RequestAccessor) ProxyEventToHTTPRequest(req events.APIGatewayProxyRequ for h := range req.Headers { httpRequest.Header.Add(h, req.Headers[h]) } + + apiGwContext, err := json.Marshal(req.RequestContext) + if err != nil { + log.Println("Could not Marshal API GW context for custom header") + return nil, err + } stageVars, err := json.Marshal(req.StageVariables) if err != nil { log.Println("Could not marshal stage variables for custom header") return nil, err } + httpRequest.Header.Add(APIGwContextHeader, string(apiGwContext)) httpRequest.Header.Add(APIGwStageVarsHeader, string(stageVars)) httpRequest = httpRequest.WithContext(toContext(httpRequest, req.RequestContext)) @@ -168,7 +181,13 @@ func (r *RequestAccessor) ProxyEventToHTTPRequest(req events.APIGatewayProxyRequ } func toContext(req *http.Request, gwContext events.APIGatewayProxyRequestContext) context.Context { - return context.WithValue(req.Context(), apiGatewayProxyRequestContextKey{}, gwContext) + return context.WithValue(req.Context(), ctxKey{}, gwContext) +} + +// GetAPIGatewayContextFromContext retrieve APIGatewayProxyRequestContext from context.Context +func (r *RequestAccessor) GetAPIGatewayContextFromContext(req *http.Request) (events.APIGatewayProxyRequestContext, bool) { + v, ok := req.Context().Value(ctxKey{}).(events.APIGatewayProxyRequestContext) + return v, ok } -type apiGatewayProxyRequestContextKey struct{} +type ctxKey struct{} diff --git a/core/request_test.go b/core/request_test.go index 2907db7..d84a865 100644 --- a/core/request_test.go +++ b/core/request_test.go @@ -94,10 +94,8 @@ var _ = Describe("RequestAccessor tests", func() { It("Populates context header correctly", func() { httpReq, err := accessor.ProxyEventToHTTPRequest(contextRequest) Expect(err).To(BeNil()) - Expect(1).To(Equal(len(httpReq.Header))) - context, err := accessor.GetAPIGatewayContext(httpReq) - Expect(err).To(BeNil()) - Expect(context).ToNot(BeNil()) + Expect(2).To(Equal(len(httpReq.Header))) + Expect(httpReq.Header.Get(core.APIGwContextHeader)).ToNot(BeNil()) }) }) @@ -134,6 +132,11 @@ var _ = Describe("RequestAccessor tests", func() { Expect("x").To(Equal(context.AccountID)) Expect("x").To(Equal(context.RequestID)) Expect("x").To(Equal(context.APIID)) + proxyContext, ok := accessor.GetAPIGatewayContextFromContext(httpReq) + Expect(ok).To(BeTrue()) + Expect("x").To(Equal(proxyContext.APIID)) + Expect("x").To(Equal(proxyContext.RequestID)) + Expect("x").To(Equal(proxyContext.APIID)) Expect("prod").To(Equal(context.Stage)) }) From 8dc29eb9fce683f94d44a6fc20440c47eb829600 Mon Sep 17 00:00:00 2001 From: Nikolay Sarychev Date: Thu, 25 Apr 2019 16:54:12 -0600 Subject: [PATCH 05/12] Refactor to way better --- chi/adapter.go | 13 ++++++++++--- chi/chilambda_test.go | 8 ++++++-- core/request.go | 35 +++++++++++++++++++++++------------ core/request_test.go | 25 +++++++++++++------------ gin/adapter.go | 10 +++++++--- gin/ginlambda_test.go | 9 +++++++-- gorillamux/adapter.go | 10 +++++++--- gorillamux/adapter_test.go | 4 ++-- handlerfunc/adapter.go | 10 +++++++--- handlerfunc/adapter_test.go | 7 ++++++- httpadapter/adapter.go | 10 +++++++--- httpadapter/adapter_test.go | 7 ++++++- negroni/adapter.go | 10 +++++++--- negroni/adapter_test.go | 6 +++--- sample/main.go | 4 ++-- 15 files changed, 113 insertions(+), 55 deletions(-) diff --git a/chi/adapter.go b/chi/adapter.go index 44afbff..291f95d 100644 --- a/chi/adapter.go +++ b/chi/adapter.go @@ -31,15 +31,22 @@ func New(chi *chi.Mux) *ChiLambda { // Proxy receives an API Gateway proxy event, transforms it into an http.Request // object, and sends it to the chi.Mux for routing. // It returns a proxy response object gneerated from the http.ResponseWriter. -func (g *ChiLambda) Proxy(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - chiRequest, err := g.ProxyEventToHTTPRequest(req) +func (g *ChiLambda) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + return g.ProxyWithContext(context.Background(), req) +} + +// ProxyWithContext receives runtime context and an API Gateway proxy event, +// transforms them into an http.Request object, and sends it to the chi.Mux for routing. +// It returns a proxy response object gneerated from the http.ResponseWriter. +func (g *ChiLambda) ProxyWithContext(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + chiRequest, err := g.ProxyEventToHTTPRequest(ctx, req) if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) } respWriter := core.NewProxyResponseWriter() - g.chiMux.ServeHTTP(http.ResponseWriter(respWriter), chiRequest.WithContext(ctx)) + g.chiMux.ServeHTTP(http.ResponseWriter(respWriter), chiRequest) proxyResponse, err := respWriter.GetProxyResponse() if err != nil { diff --git a/chi/chilambda_test.go b/chi/chilambda_test.go index 7f7080a..7e6fba4 100644 --- a/chi/chilambda_test.go +++ b/chi/chilambda_test.go @@ -6,7 +6,7 @@ import ( "net/http" "github.com/aws/aws-lambda-go/events" - "github.com/awslabs/aws-lambda-go-api-proxy/chi" + chiadapter "github.com/awslabs/aws-lambda-go-api-proxy/chi" "github.com/go-chi/chi" . "github.com/onsi/ginkgo" @@ -30,10 +30,14 @@ var _ = Describe("ChiLambda tests", func() { HTTPMethod: "GET", } - resp, err := adapter.Proxy(context.Background(), req) + resp, err := adapter.ProxyWithContext(context.Background(), req) Expect(err).To(BeNil()) Expect(resp.StatusCode).To(Equal(200)) + + resp, err = adapter.Proxy(req) + Expect(err).To(BeNil()) + Expect(resp.StatusCode).To(Equal(200)) }) }) }) diff --git a/core/request.go b/core/request.go index cfb68c1..55ebd39 100644 --- a/core/request.go +++ b/core/request.go @@ -16,6 +16,7 @@ import ( "strings" "github.com/aws/aws-lambda-go/events" + "github.com/aws/aws-lambda-go/lambdacontext" ) // CustomHostVariable is the name of the environment variable that contains @@ -105,11 +106,12 @@ func (r *RequestAccessor) StripBasePath(basePath string) string { // ProxyEventToHTTPRequest converts an API Gateway proxy event into an // http.Request object. -// Returns the populated request with an additional two custom headers for the -// stage variables and API Gateway context. To access these properties use -// the GetAPIGatewayStageVars and GetAPIGatewayContext method of the RequestAccessor -// object. -func (r *RequestAccessor) ProxyEventToHTTPRequest(req events.APIGatewayProxyRequest) (*http.Request, error) { +// Returns the populated request with: +// * an additional two custom headers for the stage variables and API Gateway context. +// To access these properties use the GetAPIGatewayStageVars and GetAPIGatewayContext method of the RequestAccessor object. +// * lambda runtime context passed as well as APIGatewayProxyRequestContext as part of it's context under different keys. +// Access those using GetAPIGatewayContextFromContext and GetRuntimeContextFromContext methods of the RequestAccessor object. +func (r *RequestAccessor) ProxyEventToHTTPRequest(ctx context.Context, req events.APIGatewayProxyRequest) (*http.Request, error) { decodedBody := []byte(req.Body) if req.IsBase64Encoded { base64Body, err := base64.StdEncoding.DecodeString(req.Body) @@ -175,19 +177,28 @@ func (r *RequestAccessor) ProxyEventToHTTPRequest(req events.APIGatewayProxyRequ } httpRequest.Header.Add(APIGwContextHeader, string(apiGwContext)) httpRequest.Header.Add(APIGwStageVarsHeader, string(stageVars)) - httpRequest = httpRequest.WithContext(toContext(httpRequest, req.RequestContext)) + lc, _ := lambdacontext.FromContext(ctx) + ctx = context.WithValue(httpRequest.Context(), ctxKey{}, requestContext{lambdaRuntime: lc, gatewayProxy: req.RequestContext}) + httpRequest = httpRequest.WithContext(ctx) return httpRequest, nil } -func toContext(req *http.Request, gwContext events.APIGatewayProxyRequestContext) context.Context { - return context.WithValue(req.Context(), ctxKey{}, gwContext) +// GetAPIGatewayContextFromContext retrieve APIGatewayProxyRequestContext from context.Context +func GetAPIGatewayContextFromContext(ctx context.Context) (events.APIGatewayProxyRequestContext, bool) { + v, ok := ctx.Value(ctxKey{}).(requestContext) + return v.gatewayProxy, ok } -// GetAPIGatewayContextFromContext retrieve APIGatewayProxyRequestContext from context.Context -func (r *RequestAccessor) GetAPIGatewayContextFromContext(req *http.Request) (events.APIGatewayProxyRequestContext, bool) { - v, ok := req.Context().Value(ctxKey{}).(events.APIGatewayProxyRequestContext) - return v, ok +// GetRuntimeContextFromContext retrieve runtime context from context.Context +func GetRuntimeContextFromContext(ctx context.Context) (*lambdacontext.LambdaContext, bool) { + v, ok := ctx.Value(ctxKey{}).(requestContext) + return v.lambdaRuntime, ok } type ctxKey struct{} + +type requestContext struct { + lambdaRuntime *lambdacontext.LambdaContext + gatewayProxy events.APIGatewayProxyRequestContext +} diff --git a/core/request_test.go b/core/request_test.go index d84a865..2241860 100644 --- a/core/request_test.go +++ b/core/request_test.go @@ -1,6 +1,7 @@ package core_test import ( + "context" "encoding/base64" "io/ioutil" "math/rand" @@ -18,7 +19,7 @@ var _ = Describe("RequestAccessor tests", func() { accessor := core.RequestAccessor{} basicRequest := getProxyRequest("/hello", "GET") It("Correctly converts a basic event", func() { - httpReq, err := accessor.ProxyEventToHTTPRequest(basicRequest) + httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), basicRequest) Expect(err).To(BeNil()) Expect("/hello").To(Equal(httpReq.URL.Path)) Expect("GET").To(Equal(httpReq.Method)) @@ -26,7 +27,7 @@ var _ = Describe("RequestAccessor tests", func() { basicRequest = getProxyRequest("/hello", "get") It("Converts method to uppercase", func() { - httpReq, err := accessor.ProxyEventToHTTPRequest(basicRequest) + httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), basicRequest) Expect(err).To(BeNil()) Expect("/hello").To(Equal(httpReq.URL.Path)) Expect("GET").To(Equal(httpReq.Method)) @@ -45,7 +46,7 @@ var _ = Describe("RequestAccessor tests", func() { binaryRequest.IsBase64Encoded = true It("Decodes a base64 encoded body", func() { - httpReq, err := accessor.ProxyEventToHTTPRequest(binaryRequest) + httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), binaryRequest) Expect(err).To(BeNil()) Expect("/hello").To(Equal(httpReq.URL.Path)) Expect("POST").To(Equal(httpReq.Method)) @@ -63,7 +64,7 @@ var _ = Describe("RequestAccessor tests", func() { "world": {"2", "3"}, } It("Populates query string correctly", func() { - httpReq, err := accessor.ProxyEventToHTTPRequest(qsRequest) + httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), qsRequest) Expect(err).To(BeNil()) Expect("/hello").To(Equal(httpReq.URL.Path)) Expect("GET").To(Equal(httpReq.Method)) @@ -83,7 +84,7 @@ var _ = Describe("RequestAccessor tests", func() { It("Stips the base path correct", func() { accessor.StripBasePath("app1") - httpReq, err := accessor.ProxyEventToHTTPRequest(basePathRequest) + httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), basePathRequest) Expect(err).To(BeNil()) Expect("/orders").To(Equal(httpReq.URL.Path)) }) @@ -92,7 +93,7 @@ var _ = Describe("RequestAccessor tests", func() { contextRequest.RequestContext = getRequestContext() It("Populates context header correctly", func() { - httpReq, err := accessor.ProxyEventToHTTPRequest(contextRequest) + httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), contextRequest) Expect(err).To(BeNil()) Expect(2).To(Equal(len(httpReq.Header))) Expect(httpReq.Header.Get(core.APIGwContextHeader)).ToNot(BeNil()) @@ -123,7 +124,7 @@ var _ = Describe("RequestAccessor tests", func() { contextRequest.RequestContext = getRequestContext() accessor := core.RequestAccessor{} - httpReq, err := accessor.ProxyEventToHTTPRequest(contextRequest) + httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), contextRequest) Expect(err).To(BeNil()) context, err := accessor.GetAPIGatewayContext(httpReq) @@ -132,7 +133,7 @@ var _ = Describe("RequestAccessor tests", func() { Expect("x").To(Equal(context.AccountID)) Expect("x").To(Equal(context.RequestID)) Expect("x").To(Equal(context.APIID)) - proxyContext, ok := accessor.GetAPIGatewayContextFromContext(httpReq) + proxyContext, ok := core.GetAPIGatewayContextFromContext(httpReq.Context()) Expect(ok).To(BeTrue()) Expect("x").To(Equal(proxyContext.APIID)) Expect("x").To(Equal(proxyContext.RequestID)) @@ -145,7 +146,7 @@ var _ = Describe("RequestAccessor tests", func() { varsRequest.StageVariables = getStageVariables() accessor := core.RequestAccessor{} - httpReq, err := accessor.ProxyEventToHTTPRequest(varsRequest) + httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), varsRequest) Expect(err).To(BeNil()) stageVars, err := accessor.GetAPIGatewayStageVars(httpReq) @@ -160,7 +161,7 @@ var _ = Describe("RequestAccessor tests", func() { It("Populates the default hostname correctly", func() { basicRequest := getProxyRequest("orders", "GET") accessor := core.RequestAccessor{} - httpReq, err := accessor.ProxyEventToHTTPRequest(basicRequest) + httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), basicRequest) Expect(err).To(BeNil()) Expect(core.DefaultServerAddress).To(Equal("https://" + httpReq.Host)) @@ -172,7 +173,7 @@ var _ = Describe("RequestAccessor tests", func() { os.Setenv(core.CustomHostVariable, myCustomHost) basicRequest := getProxyRequest("orders", "GET") accessor := core.RequestAccessor{} - httpReq, err := accessor.ProxyEventToHTTPRequest(basicRequest) + httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), basicRequest) Expect(err).To(BeNil()) Expect(myCustomHost).To(Equal("http://" + httpReq.Host)) @@ -185,7 +186,7 @@ var _ = Describe("RequestAccessor tests", func() { os.Setenv(core.CustomHostVariable, myCustomHost+"/") basicRequest := getProxyRequest("orders", "GET") accessor := core.RequestAccessor{} - httpReq, err := accessor.ProxyEventToHTTPRequest(basicRequest) + httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), basicRequest) Expect(err).To(BeNil()) Expect(myCustomHost).To(Equal("http://" + httpReq.Host)) diff --git a/gin/adapter.go b/gin/adapter.go index 7ee2d20..1896516 100644 --- a/gin/adapter.go +++ b/gin/adapter.go @@ -28,18 +28,22 @@ func New(gin *gin.Engine) *GinLambda { return &GinLambda{ginEngine: gin} } +func (g *GinLambda) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + return g.ProxyWithContext(context.Background(), req) +} + // Proxy receives an API Gateway proxy event, transforms it into an http.Request // object, and sends it to the gin.Engine for routing. // It returns a proxy response object gneerated from the http.ResponseWriter. -func (g *GinLambda) Proxy(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - ginRequest, err := g.ProxyEventToHTTPRequest(req) +func (g *GinLambda) ProxyWithContext(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + ginRequest, err := g.ProxyEventToHTTPRequest(ctx, req) if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) } respWriter := core.NewProxyResponseWriter() - g.ginEngine.ServeHTTP(http.ResponseWriter(respWriter), ginRequest.WithContext(ctx)) + g.ginEngine.ServeHTTP(http.ResponseWriter(respWriter), ginRequest) proxyResponse, err := respWriter.GetProxyResponse() if err != nil { diff --git a/gin/ginlambda_test.go b/gin/ginlambda_test.go index 4091be9..db90c76 100644 --- a/gin/ginlambda_test.go +++ b/gin/ginlambda_test.go @@ -5,7 +5,7 @@ import ( "log" "github.com/aws/aws-lambda-go/events" - "github.com/awslabs/aws-lambda-go-api-proxy/gin" + ginadapter "github.com/awslabs/aws-lambda-go-api-proxy/gin" "github.com/gin-gonic/gin" . "github.com/onsi/ginkgo" @@ -31,7 +31,12 @@ var _ = Describe("GinLambda tests", func() { HTTPMethod: "GET", } - resp, err := adapter.Proxy(context.Background(), req) + resp, err := adapter.ProxyWithContext(context.Background(), req) + + Expect(err).To(BeNil()) + Expect(resp.StatusCode).To(Equal(200)) + + resp, err = adapter.Proxy(req) Expect(err).To(BeNil()) Expect(resp.StatusCode).To(Equal(200)) diff --git a/gorillamux/adapter.go b/gorillamux/adapter.go index ebf2cfe..4e87b42 100644 --- a/gorillamux/adapter.go +++ b/gorillamux/adapter.go @@ -20,14 +20,18 @@ func New(router *mux.Router) *GorillaMuxAdapter { } } -func (h *GorillaMuxAdapter) Proxy(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - req, err := h.ProxyEventToHTTPRequest(event) +func (h *GorillaMuxAdapter) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + return h.ProxyWithContext(context.Background(), req) +} + +func (h *GorillaMuxAdapter) ProxyWithContext(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + req, err := h.ProxyEventToHTTPRequest(ctx, event) if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) } w := core.NewProxyResponseWriter() - h.router.ServeHTTP(http.ResponseWriter(w), req.WithContext(ctx)) + h.router.ServeHTTP(http.ResponseWriter(w), req) resp, err := w.GetProxyResponse() if err != nil { diff --git a/gorillamux/adapter_test.go b/gorillamux/adapter_test.go index 54d9979..fbc9a63 100644 --- a/gorillamux/adapter_test.go +++ b/gorillamux/adapter_test.go @@ -37,7 +37,7 @@ var _ = Describe("GorillaMuxAdapter tests", func() { HTTPMethod: "GET", } - homePageResp, homePageReqErr := adapter.Proxy(context.Background(), homePageReq) + homePageResp, homePageReqErr := adapter.ProxyWithContext(context.Background(), homePageReq) Expect(homePageReqErr).To(BeNil()) Expect(homePageResp.StatusCode).To(Equal(200)) @@ -48,7 +48,7 @@ var _ = Describe("GorillaMuxAdapter tests", func() { HTTPMethod: "GET", } - productsPageResp, productsPageReqErr := adapter.Proxy(context.Background(), productsPageReq) + productsPageResp, productsPageReqErr := adapter.Proxy(productsPageReq) Expect(productsPageReqErr).To(BeNil()) Expect(productsPageResp.StatusCode).To(Equal(200)) diff --git a/handlerfunc/adapter.go b/handlerfunc/adapter.go index 1770847..ca31165 100644 --- a/handlerfunc/adapter.go +++ b/handlerfunc/adapter.go @@ -19,14 +19,18 @@ func New(handlerFunc http.HandlerFunc) *HandlerFuncAdapter { } } -func (h *HandlerFuncAdapter) Proxy(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - req, err := h.ProxyEventToHTTPRequest(event) +func (h *HandlerFuncAdapter) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + return h.ProxyWithContext(context.Background(), req) +} + +func (h *HandlerFuncAdapter) ProxyWithContext(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + req, err := h.ProxyEventToHTTPRequest(ctx, event) if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) } w := core.NewProxyResponseWriter() - h.handlerFunc.ServeHTTP(http.ResponseWriter(w), req.WithContext(ctx)) + h.handlerFunc.ServeHTTP(http.ResponseWriter(w), req) resp, err := w.GetProxyResponse() if err != nil { diff --git a/handlerfunc/adapter_test.go b/handlerfunc/adapter_test.go index cadd53b..15cca0d 100644 --- a/handlerfunc/adapter_test.go +++ b/handlerfunc/adapter_test.go @@ -30,7 +30,12 @@ var _ = Describe("HandlerFuncAdapter tests", func() { HTTPMethod: "GET", } - resp, err := adapter.Proxy(context.Background(), req) + resp, err := adapter.ProxyWithContext(context.Background(), req) + + Expect(err).To(BeNil()) + Expect(resp.StatusCode).To(Equal(200)) + + resp, err = adapter.Proxy(req) Expect(err).To(BeNil()) Expect(resp.StatusCode).To(Equal(200)) diff --git a/httpadapter/adapter.go b/httpadapter/adapter.go index 2c28912..9468716 100644 --- a/httpadapter/adapter.go +++ b/httpadapter/adapter.go @@ -19,14 +19,18 @@ func New(handler http.Handler) *HandlerAdapter { } } -func (h *HandlerAdapter) Proxy(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - req, err := h.ProxyEventToHTTPRequest(event) +func (h *HandlerAdapter) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + return h.ProxyWithContext(context.Background(), req) +} + +func (h *HandlerAdapter) ProxyWithContext(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + req, err := h.ProxyEventToHTTPRequest(ctx, event) if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) } w := core.NewProxyResponseWriter() - h.handler.ServeHTTP(http.ResponseWriter(w), req.WithContext(ctx)) + h.handler.ServeHTTP(http.ResponseWriter(w), req) resp, err := w.GetProxyResponse() if err != nil { diff --git a/httpadapter/adapter_test.go b/httpadapter/adapter_test.go index a03b52f..4aa3f3f 100644 --- a/httpadapter/adapter_test.go +++ b/httpadapter/adapter_test.go @@ -37,7 +37,12 @@ var _ = Describe("HTTPAdapter tests", func() { HTTPMethod: "GET", } - resp, err := adapter.Proxy(context.Background(), req) + resp, err := adapter.ProxyWithContext(context.Background(), req) + + Expect(err).To(BeNil()) + Expect(resp.StatusCode).To(Equal(200)) + + resp, err = adapter.Proxy(req) Expect(err).To(BeNil()) Expect(resp.StatusCode).To(Equal(200)) diff --git a/negroni/adapter.go b/negroni/adapter.go index 6ca06e3..31c633f 100644 --- a/negroni/adapter.go +++ b/negroni/adapter.go @@ -20,14 +20,18 @@ func New(n *negroni.Negroni) *NegroniAdapter { } } -func (h *NegroniAdapter) Proxy(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - req, err := h.ProxyEventToHTTPRequest(event) +func (h *NegroniAdapter) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + return h.ProxyWithContext(context.Background(), req) +} + +func (h *NegroniAdapter) ProxyWithContext(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + req, err := h.ProxyEventToHTTPRequest(ctx, event) if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) } w := core.NewProxyResponseWriter() - h.n.ServeHTTP(http.ResponseWriter(w), req.WithContext(ctx)) + h.n.ServeHTTP(http.ResponseWriter(w), req) resp, err := w.GetProxyResponse() if err != nil { diff --git a/negroni/adapter_test.go b/negroni/adapter_test.go index d689427..b1a8df4 100644 --- a/negroni/adapter_test.go +++ b/negroni/adapter_test.go @@ -7,7 +7,7 @@ import ( "net/http" "github.com/aws/aws-lambda-go/events" - "github.com/awslabs/aws-lambda-go-api-proxy/negroni" + negroniadapter "github.com/awslabs/aws-lambda-go-api-proxy/negroni" "github.com/urfave/negroni" . "github.com/onsi/ginkgo" @@ -43,7 +43,7 @@ var _ = Describe("NegroniAdapter tests", func() { HTTPMethod: "GET", } - homePageResp, homePageReqErr := adapter.Proxy(context.Background(), homePageReq) + homePageResp, homePageReqErr := adapter.ProxyWithContext(context.Background(), homePageReq) Expect(homePageReqErr).To(BeNil()) Expect(homePageResp.StatusCode).To(Equal(200)) @@ -54,7 +54,7 @@ var _ = Describe("NegroniAdapter tests", func() { HTTPMethod: "GET", } - productsPageResp, productsPageReqErr := adapter.Proxy(context.Background(), productsPageReq) + productsPageResp, productsPageReqErr := adapter.Proxy(productsPageReq) Expect(productsPageReqErr).To(BeNil()) Expect(productsPageResp.StatusCode).To(Equal(200)) diff --git a/sample/main.go b/sample/main.go index 4b20f98..527b0e5 100644 --- a/sample/main.go +++ b/sample/main.go @@ -8,7 +8,7 @@ import ( "github.com/aws/aws-lambda-go/events" "github.com/aws/aws-lambda-go/lambda" - "github.com/awslabs/aws-lambda-go-api-proxy/gin" + ginadapter "github.com/awslabs/aws-lambda-go-api-proxy/gin" "github.com/gin-gonic/gin" ) @@ -28,7 +28,7 @@ func Handler(ctx context.Context, req events.APIGatewayProxyRequest) (events.API ginLambda = ginadapter.New(r) } - return ginLambda.Proxy(ctx, req) + return ginLambda.ProxyWithContext(ctx, req) } func main() { From 582fd943dd0b6e197026f06b155e575c66e508ff Mon Sep 17 00:00:00 2001 From: Nikolay Sarychev Date: Fri, 26 Apr 2019 10:08:10 -0600 Subject: [PATCH 06/12] Cleanup --- core/request.go | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/core/request.go b/core/request.go index 55ebd39..87fa55f 100644 --- a/core/request.go +++ b/core/request.go @@ -110,7 +110,7 @@ func (r *RequestAccessor) StripBasePath(basePath string) string { // * an additional two custom headers for the stage variables and API Gateway context. // To access these properties use the GetAPIGatewayStageVars and GetAPIGatewayContext method of the RequestAccessor object. // * lambda runtime context passed as well as APIGatewayProxyRequestContext as part of it's context under different keys. -// Access those using GetAPIGatewayContextFromContext and GetRuntimeContextFromContext methods of the RequestAccessor object. +// Access those using GetAPIGatewayContextFromContext and GetRuntimeContextFromContext methods in this package. func (r *RequestAccessor) ProxyEventToHTTPRequest(ctx context.Context, req events.APIGatewayProxyRequest) (*http.Request, error) { decodedBody := []byte(req.Body) if req.IsBase64Encoded { @@ -179,24 +179,26 @@ func (r *RequestAccessor) ProxyEventToHTTPRequest(ctx context.Context, req event httpRequest.Header.Add(APIGwStageVarsHeader, string(stageVars)) lc, _ := lambdacontext.FromContext(ctx) - ctx = context.WithValue(httpRequest.Context(), ctxKey{}, requestContext{lambdaRuntime: lc, gatewayProxy: req.RequestContext}) + rc := requestContext{lambdaRuntime: lc, gatewayProxy: req.RequestContext} + ctx = context.WithValue(httpRequest.Context(), requestContextKey{}, + rc) httpRequest = httpRequest.WithContext(ctx) return httpRequest, nil } // GetAPIGatewayContextFromContext retrieve APIGatewayProxyRequestContext from context.Context func GetAPIGatewayContextFromContext(ctx context.Context) (events.APIGatewayProxyRequestContext, bool) { - v, ok := ctx.Value(ctxKey{}).(requestContext) + v, ok := ctx.Value(requestContextKey{}).(requestContext) return v.gatewayProxy, ok } -// GetRuntimeContextFromContext retrieve runtime context from context.Context +// GetRuntimeContextFromContext retrieve Lambda Runtime Context from context.Context func GetRuntimeContextFromContext(ctx context.Context) (*lambdacontext.LambdaContext, bool) { - v, ok := ctx.Value(ctxKey{}).(requestContext) + v, ok := ctx.Value(requestContextKey{}).(requestContext) return v.lambdaRuntime, ok } -type ctxKey struct{} +type requestContextKey struct{} type requestContext struct { lambdaRuntime *lambdacontext.LambdaContext From 55d7e20a4cfaa7de44aef904e70720b77e12ac8e Mon Sep 17 00:00:00 2001 From: Nikolay Sarychev Date: Fri, 26 Apr 2019 10:19:16 -0600 Subject: [PATCH 07/12] Add comments --- chi/adapter.go | 2 +- gin/adapter.go | 9 ++++++--- gorillamux/adapter.go | 6 ++++++ handlerfunc/adapter.go | 6 ++++++ httpadapter/adapter.go | 6 ++++++ negroni/adapter.go | 6 ++++++ 6 files changed, 31 insertions(+), 4 deletions(-) diff --git a/chi/adapter.go b/chi/adapter.go index 291f95d..a051fea 100644 --- a/chi/adapter.go +++ b/chi/adapter.go @@ -37,7 +37,7 @@ func (g *ChiLambda) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayP // ProxyWithContext receives runtime context and an API Gateway proxy event, // transforms them into an http.Request object, and sends it to the chi.Mux for routing. -// It returns a proxy response object gneerated from the http.ResponseWriter. +// It returns a proxy response object generated from the http.ResponseWriter. func (g *ChiLambda) ProxyWithContext(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { chiRequest, err := g.ProxyEventToHTTPRequest(ctx, req) diff --git a/gin/adapter.go b/gin/adapter.go index 1896516..195d037 100644 --- a/gin/adapter.go +++ b/gin/adapter.go @@ -28,13 +28,16 @@ func New(gin *gin.Engine) *GinLambda { return &GinLambda{ginEngine: gin} } +// Proxy receives an API Gateway proxy event, transforms it into an http.Request +// object, and sends it to the gin.Engine for routing. +// It returns a proxy response object gneerated from the http.ResponseWriter. func (g *GinLambda) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { return g.ProxyWithContext(context.Background(), req) } -// Proxy receives an API Gateway proxy event, transforms it into an http.Request -// object, and sends it to the gin.Engine for routing. -// It returns a proxy response object gneerated from the http.ResponseWriter. +// ProxyWithContext receives runtime context and an API Gateway proxy event, +// transforms them into an http.Request object, and sends it to the gin.Engine for routing. +// It returns a proxy response object generated from the http.ResponseWriter. func (g *GinLambda) ProxyWithContext(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { ginRequest, err := g.ProxyEventToHTTPRequest(ctx, req) diff --git a/gorillamux/adapter.go b/gorillamux/adapter.go index 4e87b42..8287809 100644 --- a/gorillamux/adapter.go +++ b/gorillamux/adapter.go @@ -20,10 +20,16 @@ func New(router *mux.Router) *GorillaMuxAdapter { } } +// Proxy receives an API Gateway proxy event, transforms it into an http.Request +// object, and sends it to the mux.Router for routing. +// It returns a proxy response object gneerated from the http.ResponseWriter. func (h *GorillaMuxAdapter) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { return h.ProxyWithContext(context.Background(), req) } +// ProxyWithContext receives runtime context and an API Gateway proxy event, +// transforms them into an http.Request object, and sends it to the mux.Router for routing. +// It returns a proxy response object generated from the http.ResponseWriter. func (h *GorillaMuxAdapter) ProxyWithContext(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { req, err := h.ProxyEventToHTTPRequest(ctx, event) if err != nil { diff --git a/handlerfunc/adapter.go b/handlerfunc/adapter.go index ca31165..6d6db73 100644 --- a/handlerfunc/adapter.go +++ b/handlerfunc/adapter.go @@ -19,10 +19,16 @@ func New(handlerFunc http.HandlerFunc) *HandlerFuncAdapter { } } +// Proxy receives an API Gateway proxy event, transforms it into an http.Request +// object, and sends it to the http.HandlerFunc for routing. +// It returns a proxy response object gneerated from the http.ResponseWriter. func (h *HandlerFuncAdapter) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { return h.ProxyWithContext(context.Background(), req) } +// ProxyWithContext receives runtime context and an API Gateway proxy event, +// transforms them into an http.Request object, and sends it to the http.HandlerFunc for routing. +// It returns a proxy response object generated from the http.ResponseWriter. func (h *HandlerFuncAdapter) ProxyWithContext(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { req, err := h.ProxyEventToHTTPRequest(ctx, event) if err != nil { diff --git a/httpadapter/adapter.go b/httpadapter/adapter.go index 9468716..f746a13 100644 --- a/httpadapter/adapter.go +++ b/httpadapter/adapter.go @@ -19,10 +19,16 @@ func New(handler http.Handler) *HandlerAdapter { } } +// Proxy receives an API Gateway proxy event, transforms it into an http.Request +// object, and sends it to the http.HandlerFunc for routing. +// It returns a proxy response object gneerated from the http.Handler. func (h *HandlerAdapter) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { return h.ProxyWithContext(context.Background(), req) } +// ProxyWithContext receives runtime context and an API Gateway proxy event, +// transforms them into an http.Request object, and sends it to the http.Handler for routing. +// It returns a proxy response object generated from the http.ResponseWriter. func (h *HandlerAdapter) ProxyWithContext(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { req, err := h.ProxyEventToHTTPRequest(ctx, event) if err != nil { diff --git a/negroni/adapter.go b/negroni/adapter.go index 31c633f..b4defe9 100644 --- a/negroni/adapter.go +++ b/negroni/adapter.go @@ -20,10 +20,16 @@ func New(n *negroni.Negroni) *NegroniAdapter { } } +// Proxy receives an API Gateway proxy event, transforms it into an http.Request +// object, and sends it to the negroni.Negroni for routing. +// It returns a proxy response object gneerated from the http.Handler. func (h *NegroniAdapter) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { return h.ProxyWithContext(context.Background(), req) } +// ProxyWithContext receives runtime context and an API Gateway proxy event, +// transforms them into an http.Request object, and sends it to the negroni.Negroni for routing. +// It returns a proxy response object generated from the http.ResponseWriter. func (h *NegroniAdapter) ProxyWithContext(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { req, err := h.ProxyEventToHTTPRequest(ctx, event) if err != nil { From f1aadb3512cbd0d3be2e752331af052bb6ceea8d Mon Sep 17 00:00:00 2001 From: Nikolay Sarychev Date: Fri, 26 Apr 2019 11:31:41 -0600 Subject: [PATCH 08/12] Add tests --- core/request.go | 10 ++++++---- core/request_test.go | 38 ++++++++++++++++++++++++++++++++------ 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/core/request.go b/core/request.go index 87fa55f..89723ae 100644 --- a/core/request.go +++ b/core/request.go @@ -180,7 +180,7 @@ func (r *RequestAccessor) ProxyEventToHTTPRequest(ctx context.Context, req event lc, _ := lambdacontext.FromContext(ctx) rc := requestContext{lambdaRuntime: lc, gatewayProxy: req.RequestContext} - ctx = context.WithValue(httpRequest.Context(), requestContextKey{}, + ctx = context.WithValue(httpRequest.Context(), requestContextKey, rc) httpRequest = httpRequest.WithContext(ctx) return httpRequest, nil @@ -188,17 +188,19 @@ func (r *RequestAccessor) ProxyEventToHTTPRequest(ctx context.Context, req event // GetAPIGatewayContextFromContext retrieve APIGatewayProxyRequestContext from context.Context func GetAPIGatewayContextFromContext(ctx context.Context) (events.APIGatewayProxyRequestContext, bool) { - v, ok := ctx.Value(requestContextKey{}).(requestContext) + v, ok := ctx.Value(requestContextKey).(requestContext) return v.gatewayProxy, ok } // GetRuntimeContextFromContext retrieve Lambda Runtime Context from context.Context func GetRuntimeContextFromContext(ctx context.Context) (*lambdacontext.LambdaContext, bool) { - v, ok := ctx.Value(requestContextKey{}).(requestContext) + v, ok := ctx.Value(requestContextKey).(requestContext) return v.lambdaRuntime, ok } -type requestContextKey struct{} +type key struct{} + +var requestContextKey = &key{} type requestContext struct { lambdaRuntime *lambdacontext.LambdaContext diff --git a/core/request_test.go b/core/request_test.go index 2241860..ed4c3fd 100644 --- a/core/request_test.go +++ b/core/request_test.go @@ -7,6 +7,8 @@ import ( "math/rand" "os" + "github.com/aws/aws-lambda-go/lambdacontext" + "github.com/aws/aws-lambda-go/events" "github.com/awslabs/aws-lambda-go-api-proxy/core" @@ -127,18 +129,42 @@ var _ = Describe("RequestAccessor tests", func() { httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), contextRequest) Expect(err).To(BeNil()) - context, err := accessor.GetAPIGatewayContext(httpReq) + headerContext, err := accessor.GetAPIGatewayContext(httpReq) Expect(err).To(BeNil()) - Expect(context).ToNot(BeNil()) - Expect("x").To(Equal(context.AccountID)) - Expect("x").To(Equal(context.RequestID)) - Expect("x").To(Equal(context.APIID)) + Expect(headerContext).ToNot(BeNil()) + Expect("x").To(Equal(headerContext.AccountID)) + Expect("x").To(Equal(headerContext.RequestID)) + Expect("x").To(Equal(headerContext.APIID)) proxyContext, ok := core.GetAPIGatewayContextFromContext(httpReq.Context()) Expect(ok).To(BeTrue()) Expect("x").To(Equal(proxyContext.APIID)) Expect("x").To(Equal(proxyContext.RequestID)) Expect("x").To(Equal(proxyContext.APIID)) - Expect("prod").To(Equal(context.Stage)) + Expect("prod").To(Equal(proxyContext.Stage)) + runtimeContext, ok := core.GetRuntimeContextFromContext(httpReq.Context()) + Expect(ok).To(BeTrue()) + Expect(runtimeContext).To(BeNil()) + + lambdaContext := lambdacontext.NewContext(context.Background(), &lambdacontext.LambdaContext{AwsRequestID: "abc123"}) + httpReq, err = accessor.ProxyEventToHTTPRequest(lambdaContext, contextRequest) + Expect(err).To(BeNil()) + + headerContext, err = accessor.GetAPIGatewayContext(httpReq) + Expect(err).To(BeNil()) + Expect(headerContext).ToNot(BeNil()) + Expect("x").To(Equal(headerContext.AccountID)) + Expect("x").To(Equal(headerContext.RequestID)) + Expect("x").To(Equal(headerContext.APIID)) + proxyContext, ok = core.GetAPIGatewayContextFromContext(httpReq.Context()) + Expect(ok).To(BeTrue()) + Expect("x").To(Equal(proxyContext.APIID)) + Expect("x").To(Equal(proxyContext.RequestID)) + Expect("x").To(Equal(proxyContext.APIID)) + Expect("prod").To(Equal(proxyContext.Stage)) + runtimeContext, ok = core.GetRuntimeContextFromContext(httpReq.Context()) + Expect(ok).To(BeTrue()) + Expect(runtimeContext).ToNot(BeNil()) + Expect("abc123").To(Equal(runtimeContext.AwsRequestID)) }) It("Populates stage variables correctly", func() { From d9e22a4fa7c05438c2bc074a5d61fd4d0b9e65c3 Mon Sep 17 00:00:00 2001 From: Nikolay Sarychev Date: Fri, 26 Apr 2019 12:21:44 -0600 Subject: [PATCH 09/12] PR comment fixes --- chi/adapter.go | 6 +++--- core/request.go | 34 ++++++++++++++++++++-------------- core/request_test.go | 28 ++++++++++++++-------------- gin/adapter.go | 6 +++--- gorillamux/adapter.go | 6 +++--- handlerfunc/adapter.go | 6 +++--- httpadapter/adapter.go | 6 +++--- negroni/adapter.go | 6 +++--- 8 files changed, 52 insertions(+), 46 deletions(-) diff --git a/chi/adapter.go b/chi/adapter.go index a051fea..1fa7984 100644 --- a/chi/adapter.go +++ b/chi/adapter.go @@ -30,16 +30,16 @@ func New(chi *chi.Mux) *ChiLambda { // Proxy receives an API Gateway proxy event, transforms it into an http.Request // object, and sends it to the chi.Mux for routing. -// It returns a proxy response object gneerated from the http.ResponseWriter. +// It returns a proxy response object generated from the http.ResponseWriter. func (g *ChiLambda) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { return g.ProxyWithContext(context.Background(), req) } -// ProxyWithContext receives runtime context and an API Gateway proxy event, +// ProxyWithContext receives context and an API Gateway proxy event, // transforms them into an http.Request object, and sends it to the chi.Mux for routing. // It returns a proxy response object generated from the http.ResponseWriter. func (g *ChiLambda) ProxyWithContext(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - chiRequest, err := g.ProxyEventToHTTPRequest(ctx, req) + chiRequest, err := g.RequestFromEvent(ctx, req) if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) diff --git a/core/request.go b/core/request.go index 89723ae..c59d32a 100644 --- a/core/request.go +++ b/core/request.go @@ -104,14 +104,23 @@ func (r *RequestAccessor) StripBasePath(basePath string) string { return newBasePath } -// ProxyEventToHTTPRequest converts an API Gateway proxy event into an -// http.Request object. +// ProxyEventToHTTPRequest converts an API Gateway proxy event into a http.Request object. // Returns the populated request with: -// * an additional two custom headers for the stage variables and API Gateway context. -// To access these properties use the GetAPIGatewayStageVars and GetAPIGatewayContext method of the RequestAccessor object. -// * lambda runtime context passed as well as APIGatewayProxyRequestContext as part of it's context under different keys. -// Access those using GetAPIGatewayContextFromContext and GetRuntimeContextFromContext methods in this package. -func (r *RequestAccessor) ProxyEventToHTTPRequest(ctx context.Context, req events.APIGatewayProxyRequest) (*http.Request, error) { +// * an additional two custom headers for the stage variables and API Gateway context. +// To access these properties use the GetAPIGatewayStageVars and GetAPIGatewayContext method of the RequestAccessor object. +// * lambda context passed as well as APIGatewayProxyRequestContext as part of its context under different keys. +// Access those using GetAPIGatewayContextFromContext and GetRuntimeContextFromContext functions in this package. +func (r *RequestAccessor) ProxyEventToHTTPRequest(req events.APIGatewayProxyRequest) (*http.Request, error) { + return r.RequestFromEvent(context.Background(), req) +} + +// RequestFromEvent converts an API Gateway proxy event and context into an http.Request object. +// Returns the populated request with: +// * an additional two custom headers for the stage variables and API Gateway context. +// To access these properties use the GetAPIGatewayStageVars and GetAPIGatewayContext method of the RequestAccessor object. +// * lambda context passed as well as APIGatewayProxyRequestContext as part of its context under different keys. +// Access those using GetAPIGatewayContextFromContext and GetRuntimeContextFromContext functions in this package. +func (r *RequestAccessor) RequestFromEvent(ctx context.Context, req events.APIGatewayProxyRequest) (*http.Request, error) { decodedBody := []byte(req.Body) if req.IsBase64Encoded { base64Body, err := base64.StdEncoding.DecodeString(req.Body) @@ -180,27 +189,24 @@ func (r *RequestAccessor) ProxyEventToHTTPRequest(ctx context.Context, req event lc, _ := lambdacontext.FromContext(ctx) rc := requestContext{lambdaRuntime: lc, gatewayProxy: req.RequestContext} - ctx = context.WithValue(httpRequest.Context(), requestContextKey, - rc) + ctx = context.WithValue(httpRequest.Context(), ctxKey{}, rc) httpRequest = httpRequest.WithContext(ctx) return httpRequest, nil } // GetAPIGatewayContextFromContext retrieve APIGatewayProxyRequestContext from context.Context func GetAPIGatewayContextFromContext(ctx context.Context) (events.APIGatewayProxyRequestContext, bool) { - v, ok := ctx.Value(requestContextKey).(requestContext) + v, ok := ctx.Value(ctxKey{}).(requestContext) return v.gatewayProxy, ok } // GetRuntimeContextFromContext retrieve Lambda Runtime Context from context.Context func GetRuntimeContextFromContext(ctx context.Context) (*lambdacontext.LambdaContext, bool) { - v, ok := ctx.Value(requestContextKey).(requestContext) + v, ok := ctx.Value(ctxKey{}).(requestContext) return v.lambdaRuntime, ok } -type key struct{} - -var requestContextKey = &key{} +type ctxKey struct{} type requestContext struct { lambdaRuntime *lambdacontext.LambdaContext diff --git a/core/request_test.go b/core/request_test.go index ed4c3fd..f495327 100644 --- a/core/request_test.go +++ b/core/request_test.go @@ -7,9 +7,8 @@ import ( "math/rand" "os" - "github.com/aws/aws-lambda-go/lambdacontext" - "github.com/aws/aws-lambda-go/events" + "github.com/aws/aws-lambda-go/lambdacontext" "github.com/awslabs/aws-lambda-go-api-proxy/core" . "github.com/onsi/ginkgo" @@ -21,7 +20,7 @@ var _ = Describe("RequestAccessor tests", func() { accessor := core.RequestAccessor{} basicRequest := getProxyRequest("/hello", "GET") It("Correctly converts a basic event", func() { - httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), basicRequest) + httpReq, err := accessor.RequestFromEvent(context.Background(), basicRequest) Expect(err).To(BeNil()) Expect("/hello").To(Equal(httpReq.URL.Path)) Expect("GET").To(Equal(httpReq.Method)) @@ -29,7 +28,7 @@ var _ = Describe("RequestAccessor tests", func() { basicRequest = getProxyRequest("/hello", "get") It("Converts method to uppercase", func() { - httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), basicRequest) + httpReq, err := accessor.RequestFromEvent(context.Background(), basicRequest) Expect(err).To(BeNil()) Expect("/hello").To(Equal(httpReq.URL.Path)) Expect("GET").To(Equal(httpReq.Method)) @@ -48,7 +47,7 @@ var _ = Describe("RequestAccessor tests", func() { binaryRequest.IsBase64Encoded = true It("Decodes a base64 encoded body", func() { - httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), binaryRequest) + httpReq, err := accessor.RequestFromEvent(context.Background(), binaryRequest) Expect(err).To(BeNil()) Expect("/hello").To(Equal(httpReq.URL.Path)) Expect("POST").To(Equal(httpReq.Method)) @@ -66,7 +65,7 @@ var _ = Describe("RequestAccessor tests", func() { "world": {"2", "3"}, } It("Populates query string correctly", func() { - httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), qsRequest) + httpReq, err := accessor.RequestFromEvent(context.Background(), qsRequest) Expect(err).To(BeNil()) Expect("/hello").To(Equal(httpReq.URL.Path)) Expect("GET").To(Equal(httpReq.Method)) @@ -86,7 +85,7 @@ var _ = Describe("RequestAccessor tests", func() { It("Stips the base path correct", func() { accessor.StripBasePath("app1") - httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), basePathRequest) + httpReq, err := accessor.RequestFromEvent(context.Background(), basePathRequest) Expect(err).To(BeNil()) Expect("/orders").To(Equal(httpReq.URL.Path)) }) @@ -95,7 +94,7 @@ var _ = Describe("RequestAccessor tests", func() { contextRequest.RequestContext = getRequestContext() It("Populates context header correctly", func() { - httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), contextRequest) + httpReq, err := accessor.RequestFromEvent(context.Background(), contextRequest) Expect(err).To(BeNil()) Expect(2).To(Equal(len(httpReq.Header))) Expect(httpReq.Header.Get(core.APIGwContextHeader)).ToNot(BeNil()) @@ -126,7 +125,8 @@ var _ = Describe("RequestAccessor tests", func() { contextRequest.RequestContext = getRequestContext() accessor := core.RequestAccessor{} - httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), contextRequest) + // calling old method to verify reverse compatibility + httpReq, err := accessor.ProxyEventToHTTPRequest(contextRequest) Expect(err).To(BeNil()) headerContext, err := accessor.GetAPIGatewayContext(httpReq) @@ -146,7 +146,7 @@ var _ = Describe("RequestAccessor tests", func() { Expect(runtimeContext).To(BeNil()) lambdaContext := lambdacontext.NewContext(context.Background(), &lambdacontext.LambdaContext{AwsRequestID: "abc123"}) - httpReq, err = accessor.ProxyEventToHTTPRequest(lambdaContext, contextRequest) + httpReq, err = accessor.RequestFromEvent(lambdaContext, contextRequest) Expect(err).To(BeNil()) headerContext, err = accessor.GetAPIGatewayContext(httpReq) @@ -172,7 +172,7 @@ var _ = Describe("RequestAccessor tests", func() { varsRequest.StageVariables = getStageVariables() accessor := core.RequestAccessor{} - httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), varsRequest) + httpReq, err := accessor.RequestFromEvent(context.Background(), varsRequest) Expect(err).To(BeNil()) stageVars, err := accessor.GetAPIGatewayStageVars(httpReq) @@ -187,7 +187,7 @@ var _ = Describe("RequestAccessor tests", func() { It("Populates the default hostname correctly", func() { basicRequest := getProxyRequest("orders", "GET") accessor := core.RequestAccessor{} - httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), basicRequest) + httpReq, err := accessor.RequestFromEvent(context.Background(), basicRequest) Expect(err).To(BeNil()) Expect(core.DefaultServerAddress).To(Equal("https://" + httpReq.Host)) @@ -199,7 +199,7 @@ var _ = Describe("RequestAccessor tests", func() { os.Setenv(core.CustomHostVariable, myCustomHost) basicRequest := getProxyRequest("orders", "GET") accessor := core.RequestAccessor{} - httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), basicRequest) + httpReq, err := accessor.RequestFromEvent(context.Background(), basicRequest) Expect(err).To(BeNil()) Expect(myCustomHost).To(Equal("http://" + httpReq.Host)) @@ -212,7 +212,7 @@ var _ = Describe("RequestAccessor tests", func() { os.Setenv(core.CustomHostVariable, myCustomHost+"/") basicRequest := getProxyRequest("orders", "GET") accessor := core.RequestAccessor{} - httpReq, err := accessor.ProxyEventToHTTPRequest(context.Background(), basicRequest) + httpReq, err := accessor.RequestFromEvent(context.Background(), basicRequest) Expect(err).To(BeNil()) Expect(myCustomHost).To(Equal("http://" + httpReq.Host)) diff --git a/gin/adapter.go b/gin/adapter.go index 195d037..fd6a33e 100644 --- a/gin/adapter.go +++ b/gin/adapter.go @@ -30,16 +30,16 @@ func New(gin *gin.Engine) *GinLambda { // Proxy receives an API Gateway proxy event, transforms it into an http.Request // object, and sends it to the gin.Engine for routing. -// It returns a proxy response object gneerated from the http.ResponseWriter. +// It returns a proxy response object generated from the http.ResponseWriter. func (g *GinLambda) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { return g.ProxyWithContext(context.Background(), req) } -// ProxyWithContext receives runtime context and an API Gateway proxy event, +// ProxyWithContext receives context and an API Gateway proxy event, // transforms them into an http.Request object, and sends it to the gin.Engine for routing. // It returns a proxy response object generated from the http.ResponseWriter. func (g *GinLambda) ProxyWithContext(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - ginRequest, err := g.ProxyEventToHTTPRequest(ctx, req) + ginRequest, err := g.RequestFromEvent(ctx, req) if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) diff --git a/gorillamux/adapter.go b/gorillamux/adapter.go index 8287809..95f567d 100644 --- a/gorillamux/adapter.go +++ b/gorillamux/adapter.go @@ -22,16 +22,16 @@ func New(router *mux.Router) *GorillaMuxAdapter { // Proxy receives an API Gateway proxy event, transforms it into an http.Request // object, and sends it to the mux.Router for routing. -// It returns a proxy response object gneerated from the http.ResponseWriter. +// It returns a proxy response object generated from the http.ResponseWriter. func (h *GorillaMuxAdapter) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { return h.ProxyWithContext(context.Background(), req) } -// ProxyWithContext receives runtime context and an API Gateway proxy event, +// ProxyWithContext receives context and an API Gateway proxy event, // transforms them into an http.Request object, and sends it to the mux.Router for routing. // It returns a proxy response object generated from the http.ResponseWriter. func (h *GorillaMuxAdapter) ProxyWithContext(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - req, err := h.ProxyEventToHTTPRequest(ctx, event) + req, err := h.RequestFromEvent(ctx, event) if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) } diff --git a/handlerfunc/adapter.go b/handlerfunc/adapter.go index 6d6db73..8e70e0a 100644 --- a/handlerfunc/adapter.go +++ b/handlerfunc/adapter.go @@ -21,16 +21,16 @@ func New(handlerFunc http.HandlerFunc) *HandlerFuncAdapter { // Proxy receives an API Gateway proxy event, transforms it into an http.Request // object, and sends it to the http.HandlerFunc for routing. -// It returns a proxy response object gneerated from the http.ResponseWriter. +// It returns a proxy response object generated from the http.ResponseWriter. func (h *HandlerFuncAdapter) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { return h.ProxyWithContext(context.Background(), req) } -// ProxyWithContext receives runtime context and an API Gateway proxy event, +// ProxyWithContext receives context and an API Gateway proxy event, // transforms them into an http.Request object, and sends it to the http.HandlerFunc for routing. // It returns a proxy response object generated from the http.ResponseWriter. func (h *HandlerFuncAdapter) ProxyWithContext(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - req, err := h.ProxyEventToHTTPRequest(ctx, event) + req, err := h.RequestFromEvent(ctx, event) if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) } diff --git a/httpadapter/adapter.go b/httpadapter/adapter.go index f746a13..1b25b10 100644 --- a/httpadapter/adapter.go +++ b/httpadapter/adapter.go @@ -21,16 +21,16 @@ func New(handler http.Handler) *HandlerAdapter { // Proxy receives an API Gateway proxy event, transforms it into an http.Request // object, and sends it to the http.HandlerFunc for routing. -// It returns a proxy response object gneerated from the http.Handler. +// It returns a proxy response object generated from the http.Handler. func (h *HandlerAdapter) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { return h.ProxyWithContext(context.Background(), req) } -// ProxyWithContext receives runtime context and an API Gateway proxy event, +// ProxyWithContext receives context and an API Gateway proxy event, // transforms them into an http.Request object, and sends it to the http.Handler for routing. // It returns a proxy response object generated from the http.ResponseWriter. func (h *HandlerAdapter) ProxyWithContext(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - req, err := h.ProxyEventToHTTPRequest(ctx, event) + req, err := h.RequestFromEvent(ctx, event) if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) } diff --git a/negroni/adapter.go b/negroni/adapter.go index b4defe9..8b065a9 100644 --- a/negroni/adapter.go +++ b/negroni/adapter.go @@ -22,16 +22,16 @@ func New(n *negroni.Negroni) *NegroniAdapter { // Proxy receives an API Gateway proxy event, transforms it into an http.Request // object, and sends it to the negroni.Negroni for routing. -// It returns a proxy response object gneerated from the http.Handler. +// It returns a proxy response object generated from the http.Handler. func (h *NegroniAdapter) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { return h.ProxyWithContext(context.Background(), req) } -// ProxyWithContext receives runtime context and an API Gateway proxy event, +// ProxyWithContext receives context and an API Gateway proxy event, // transforms them into an http.Request object, and sends it to the negroni.Negroni for routing. // It returns a proxy response object generated from the http.ResponseWriter. func (h *NegroniAdapter) ProxyWithContext(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - req, err := h.ProxyEventToHTTPRequest(ctx, event) + req, err := h.RequestFromEvent(ctx, event) if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) } From d93ac450084ab140ecafc35c9a4021934ee1c555 Mon Sep 17 00:00:00 2001 From: Nikolay Sarychev Date: Fri, 26 Apr 2019 16:27:21 -0600 Subject: [PATCH 10/12] Inbtroduce two paths --- core/request.go | 81 ++++++++++++++++++++++++++++---------------- core/request_test.go | 58 ++++++++++++++++++++++--------- 2 files changed, 94 insertions(+), 45 deletions(-) diff --git a/core/request.go b/core/request.go index c59d32a..6ddc1f3 100644 --- a/core/request.go +++ b/core/request.go @@ -105,22 +105,32 @@ func (r *RequestAccessor) StripBasePath(basePath string) string { } // ProxyEventToHTTPRequest converts an API Gateway proxy event into a http.Request object. -// Returns the populated request with: -// * an additional two custom headers for the stage variables and API Gateway context. -// To access these properties use the GetAPIGatewayStageVars and GetAPIGatewayContext method of the RequestAccessor object. -// * lambda context passed as well as APIGatewayProxyRequestContext as part of its context under different keys. -// Access those using GetAPIGatewayContextFromContext and GetRuntimeContextFromContext functions in this package. +// Returns the populated http request with additional two custom headers for the stage variables and API Gateway context. +// To access these properties use the GetAPIGatewayStageVars and GetAPIGatewayContext method of the RequestAccessor object. func (r *RequestAccessor) ProxyEventToHTTPRequest(req events.APIGatewayProxyRequest) (*http.Request, error) { - return r.RequestFromEvent(context.Background(), req) + httpRequest, err := r.RequestFromEvent(req) + if err != nil { + log.Println(err) + return nil, err + } + return AddToHeader(httpRequest, req) +} + +// EventToRequestWithContext converts an API Gateway proxy event and context into an http.Request object. +// Returns the populated http request with lambda context, stage variables and APIGatewayProxyRequestContext as part of its context. +// Access those using GetAPIGatewayContextFromContext, GetStageVarsFromContext and GetRuntimeContextFromContext functions in this package. +func (r *RequestAccessor) EventToRequestWithContext(ctx context.Context, req events.APIGatewayProxyRequest) (*http.Request, error) { + httpRequest, err := r.RequestFromEvent(req) + if err != nil { + log.Println(err) + return nil, err + } + return AddToContext(ctx, httpRequest, req), nil } -// RequestFromEvent converts an API Gateway proxy event and context into an http.Request object. -// Returns the populated request with: -// * an additional two custom headers for the stage variables and API Gateway context. -// To access these properties use the GetAPIGatewayStageVars and GetAPIGatewayContext method of the RequestAccessor object. -// * lambda context passed as well as APIGatewayProxyRequestContext as part of its context under different keys. -// Access those using GetAPIGatewayContextFromContext and GetRuntimeContextFromContext functions in this package. -func (r *RequestAccessor) RequestFromEvent(ctx context.Context, req events.APIGatewayProxyRequest) (*http.Request, error) { +// RequestFromEvent converts an API Gateway proxy event into an http.Request object. +// Returns the populated request maintaining headers +func (r *RequestAccessor) RequestFromEvent(req events.APIGatewayProxyRequest) (*http.Request, error) { decodedBody := []byte(req.Body) if req.IsBase64Encoded { base64Body, err := base64.StdEncoding.DecodeString(req.Body) @@ -169,46 +179,59 @@ func (r *RequestAccessor) RequestFromEvent(ctx context.Context, req events.APIGa log.Println(err) return nil, err } - for h := range req.Headers { httpRequest.Header.Add(h, req.Headers[h]) } + return httpRequest, nil +} - apiGwContext, err := json.Marshal(req.RequestContext) +// AddToHeader adds API gateway context and stage variables to headers on http request +func AddToHeader(req *http.Request, apiGwRequest events.APIGatewayProxyRequest) (*http.Request, error) { + stageVars, err := json.Marshal(apiGwRequest.StageVariables) if err != nil { - log.Println("Could not Marshal API GW context for custom header") + log.Println("Could not marshal stage variables for custom header") return nil, err } - stageVars, err := json.Marshal(req.StageVariables) + req.Header.Add(APIGwStageVarsHeader, string(stageVars)) + apiGwContext, err := json.Marshal(apiGwRequest.RequestContext) if err != nil { - log.Println("Could not marshal stage variables for custom header") - return nil, err + log.Println("Could not Marshal API GW context for custom header") + return req, err } - httpRequest.Header.Add(APIGwContextHeader, string(apiGwContext)) - httpRequest.Header.Add(APIGwStageVarsHeader, string(stageVars)) + req.Header.Add(APIGwContextHeader, string(apiGwContext)) + return req, nil +} +// AddToContext adds context, API gateway context and stage variables to context on http request +func AddToContext(ctx context.Context, req *http.Request, apiGwRequest events.APIGatewayProxyRequest) *http.Request { lc, _ := lambdacontext.FromContext(ctx) - rc := requestContext{lambdaRuntime: lc, gatewayProxy: req.RequestContext} - ctx = context.WithValue(httpRequest.Context(), ctxKey{}, rc) - httpRequest = httpRequest.WithContext(ctx) - return httpRequest, nil + rc := requestContext{lambdaContext: lc, gatewayProxyContext: apiGwRequest.RequestContext, stageVars: apiGwRequest.StageVariables} + ctx = context.WithValue(req.Context(), ctxKey{}, rc) + return req.WithContext(ctx) } // GetAPIGatewayContextFromContext retrieve APIGatewayProxyRequestContext from context.Context func GetAPIGatewayContextFromContext(ctx context.Context) (events.APIGatewayProxyRequestContext, bool) { v, ok := ctx.Value(ctxKey{}).(requestContext) - return v.gatewayProxy, ok + return v.gatewayProxyContext, ok } // GetRuntimeContextFromContext retrieve Lambda Runtime Context from context.Context func GetRuntimeContextFromContext(ctx context.Context) (*lambdacontext.LambdaContext, bool) { v, ok := ctx.Value(ctxKey{}).(requestContext) - return v.lambdaRuntime, ok + return v.lambdaContext, ok +} + +// GetStageVarsFromContext retrieve stage variables from context +func GetStageVarsFromContext(ctx context.Context) (map[string]string, bool) { + v, ok := ctx.Value(ctxKey{}).(requestContext) + return v.stageVars, ok } type ctxKey struct{} type requestContext struct { - lambdaRuntime *lambdacontext.LambdaContext - gatewayProxy events.APIGatewayProxyRequestContext + lambdaContext *lambdacontext.LambdaContext + gatewayProxyContext events.APIGatewayProxyRequestContext + stageVars map[string]string } diff --git a/core/request_test.go b/core/request_test.go index f495327..126ca59 100644 --- a/core/request_test.go +++ b/core/request_test.go @@ -20,7 +20,7 @@ var _ = Describe("RequestAccessor tests", func() { accessor := core.RequestAccessor{} basicRequest := getProxyRequest("/hello", "GET") It("Correctly converts a basic event", func() { - httpReq, err := accessor.RequestFromEvent(context.Background(), basicRequest) + httpReq, err := accessor.EventToRequestWithContext(context.Background(), basicRequest) Expect(err).To(BeNil()) Expect("/hello").To(Equal(httpReq.URL.Path)) Expect("GET").To(Equal(httpReq.Method)) @@ -28,7 +28,8 @@ var _ = Describe("RequestAccessor tests", func() { basicRequest = getProxyRequest("/hello", "get") It("Converts method to uppercase", func() { - httpReq, err := accessor.RequestFromEvent(context.Background(), basicRequest) + // calling old method to verify reverse compatibility + httpReq, err := accessor.ProxyEventToHTTPRequest(basicRequest) Expect(err).To(BeNil()) Expect("/hello").To(Equal(httpReq.URL.Path)) Expect("GET").To(Equal(httpReq.Method)) @@ -47,7 +48,7 @@ var _ = Describe("RequestAccessor tests", func() { binaryRequest.IsBase64Encoded = true It("Decodes a base64 encoded body", func() { - httpReq, err := accessor.RequestFromEvent(context.Background(), binaryRequest) + httpReq, err := accessor.EventToRequestWithContext(context.Background(), binaryRequest) Expect(err).To(BeNil()) Expect("/hello").To(Equal(httpReq.URL.Path)) Expect("POST").To(Equal(httpReq.Method)) @@ -65,7 +66,7 @@ var _ = Describe("RequestAccessor tests", func() { "world": {"2", "3"}, } It("Populates query string correctly", func() { - httpReq, err := accessor.RequestFromEvent(context.Background(), qsRequest) + httpReq, err := accessor.EventToRequestWithContext(context.Background(), qsRequest) Expect(err).To(BeNil()) Expect("/hello").To(Equal(httpReq.URL.Path)) Expect("GET").To(Equal(httpReq.Method)) @@ -85,7 +86,8 @@ var _ = Describe("RequestAccessor tests", func() { It("Stips the base path correct", func() { accessor.StripBasePath("app1") - httpReq, err := accessor.RequestFromEvent(context.Background(), basePathRequest) + httpReq, err := accessor.EventToRequestWithContext(context.Background(), basePathRequest) + Expect(err).To(BeNil()) Expect("/orders").To(Equal(httpReq.URL.Path)) }) @@ -94,7 +96,8 @@ var _ = Describe("RequestAccessor tests", func() { contextRequest.RequestContext = getRequestContext() It("Populates context header correctly", func() { - httpReq, err := accessor.RequestFromEvent(context.Background(), contextRequest) + // calling old method to verify reverse compatibility + httpReq, err := accessor.ProxyEventToHTTPRequest(contextRequest) Expect(err).To(BeNil()) Expect(2).To(Equal(len(httpReq.Header))) Expect(httpReq.Header.Get(core.APIGwContextHeader)).ToNot(BeNil()) @@ -136,6 +139,12 @@ var _ = Describe("RequestAccessor tests", func() { Expect("x").To(Equal(headerContext.RequestID)) Expect("x").To(Equal(headerContext.APIID)) proxyContext, ok := core.GetAPIGatewayContextFromContext(httpReq.Context()) + // should fail because using header proxy method + Expect(ok).To(BeFalse()) + + httpReq, err = accessor.EventToRequestWithContext(context.Background(), contextRequest) + Expect(err).To(BeNil()) + proxyContext, ok = core.GetAPIGatewayContextFromContext(httpReq.Context()) Expect(ok).To(BeTrue()) Expect("x").To(Equal(proxyContext.APIID)) Expect("x").To(Equal(proxyContext.RequestID)) @@ -146,15 +155,12 @@ var _ = Describe("RequestAccessor tests", func() { Expect(runtimeContext).To(BeNil()) lambdaContext := lambdacontext.NewContext(context.Background(), &lambdacontext.LambdaContext{AwsRequestID: "abc123"}) - httpReq, err = accessor.RequestFromEvent(lambdaContext, contextRequest) + httpReq, err = accessor.EventToRequestWithContext(lambdaContext, contextRequest) Expect(err).To(BeNil()) headerContext, err = accessor.GetAPIGatewayContext(httpReq) - Expect(err).To(BeNil()) - Expect(headerContext).ToNot(BeNil()) - Expect("x").To(Equal(headerContext.AccountID)) - Expect("x").To(Equal(headerContext.RequestID)) - Expect("x").To(Equal(headerContext.APIID)) + // should fail as new context method doesn't populate headers + Expect(err).ToNot(BeNil()) proxyContext, ok = core.GetAPIGatewayContextFromContext(httpReq.Context()) Expect(ok).To(BeTrue()) Expect("x").To(Equal(proxyContext.APIID)) @@ -172,7 +178,7 @@ var _ = Describe("RequestAccessor tests", func() { varsRequest.StageVariables = getStageVariables() accessor := core.RequestAccessor{} - httpReq, err := accessor.RequestFromEvent(context.Background(), varsRequest) + httpReq, err := accessor.ProxyEventToHTTPRequest(varsRequest) Expect(err).To(BeNil()) stageVars, err := accessor.GetAPIGatewayStageVars(httpReq) @@ -182,12 +188,32 @@ var _ = Describe("RequestAccessor tests", func() { Expect(stageVars["var2"]).ToNot(BeNil()) Expect("value1").To(Equal(stageVars["var1"])) Expect("value2").To(Equal(stageVars["var2"])) + + stageVars, ok := core.GetStageVarsFromContext(httpReq.Context()) + // not present in context + Expect(ok).To(BeFalse()) + + httpReq, err = accessor.EventToRequestWithContext(context.Background(), varsRequest) + Expect(err).To(BeNil()) + + stageVars, err = accessor.GetAPIGatewayStageVars(httpReq) + // should not be in headers + Expect(err).ToNot(BeNil()) + + stageVars, ok = core.GetStageVarsFromContext(httpReq.Context()) + Expect(ok).To(BeTrue()) + Expect(2).To(Equal(len(stageVars))) + Expect(stageVars["var1"]).ToNot(BeNil()) + Expect(stageVars["var2"]).ToNot(BeNil()) + Expect("value1").To(Equal(stageVars["var1"])) + Expect("value2").To(Equal(stageVars["var2"])) }) It("Populates the default hostname correctly", func() { + basicRequest := getProxyRequest("orders", "GET") accessor := core.RequestAccessor{} - httpReq, err := accessor.RequestFromEvent(context.Background(), basicRequest) + httpReq, err := accessor.ProxyEventToHTTPRequest(basicRequest) Expect(err).To(BeNil()) Expect(core.DefaultServerAddress).To(Equal("https://" + httpReq.Host)) @@ -199,7 +225,7 @@ var _ = Describe("RequestAccessor tests", func() { os.Setenv(core.CustomHostVariable, myCustomHost) basicRequest := getProxyRequest("orders", "GET") accessor := core.RequestAccessor{} - httpReq, err := accessor.RequestFromEvent(context.Background(), basicRequest) + httpReq, err := accessor.EventToRequestWithContext(context.Background(), basicRequest) Expect(err).To(BeNil()) Expect(myCustomHost).To(Equal("http://" + httpReq.Host)) @@ -212,7 +238,7 @@ var _ = Describe("RequestAccessor tests", func() { os.Setenv(core.CustomHostVariable, myCustomHost+"/") basicRequest := getProxyRequest("orders", "GET") accessor := core.RequestAccessor{} - httpReq, err := accessor.RequestFromEvent(context.Background(), basicRequest) + httpReq, err := accessor.EventToRequestWithContext(context.Background(), basicRequest) Expect(err).To(BeNil()) Expect(myCustomHost).To(Equal("http://" + httpReq.Host)) From f2a162da7821db92ea4d6056504754db1c09c1d2 Mon Sep 17 00:00:00 2001 From: Nikolay Sarychev Date: Fri, 26 Apr 2019 16:41:58 -0600 Subject: [PATCH 11/12] Refactor adapters --- chi/adapter.go | 9 +++++++-- gin/adapter.go | 11 ++++++++--- gorillamux/adapter.go | 11 ++++++++--- handlerfunc/adapter.go | 11 ++++++++--- httpadapter/adapter.go | 11 ++++++++--- negroni/adapter.go | 11 ++++++++--- 6 files changed, 47 insertions(+), 17 deletions(-) diff --git a/chi/adapter.go b/chi/adapter.go index 1fa7984..f578a1c 100644 --- a/chi/adapter.go +++ b/chi/adapter.go @@ -32,14 +32,19 @@ func New(chi *chi.Mux) *ChiLambda { // object, and sends it to the chi.Mux for routing. // It returns a proxy response object generated from the http.ResponseWriter. func (g *ChiLambda) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - return g.ProxyWithContext(context.Background(), req) + chiRequest, err := g.ProxyEventToHTTPRequest(req) + return g.proxyInternal(chiRequest, err) } // ProxyWithContext receives context and an API Gateway proxy event, // transforms them into an http.Request object, and sends it to the chi.Mux for routing. // It returns a proxy response object generated from the http.ResponseWriter. func (g *ChiLambda) ProxyWithContext(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - chiRequest, err := g.RequestFromEvent(ctx, req) + chiRequest, err := g.EventToRequestWithContext(ctx, req) + return g.proxyInternal(chiRequest, err) +} + +func (g *ChiLambda) proxyInternal(chiRequest *http.Request, err error) (events.APIGatewayProxyResponse, error) { if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) diff --git a/gin/adapter.go b/gin/adapter.go index fd6a33e..3218fc7 100644 --- a/gin/adapter.go +++ b/gin/adapter.go @@ -32,21 +32,26 @@ func New(gin *gin.Engine) *GinLambda { // object, and sends it to the gin.Engine for routing. // It returns a proxy response object generated from the http.ResponseWriter. func (g *GinLambda) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - return g.ProxyWithContext(context.Background(), req) + ginRequest, err := g.ProxyEventToHTTPRequest(req) + return g.proxyInternal(ginRequest, err) } // ProxyWithContext receives context and an API Gateway proxy event, // transforms them into an http.Request object, and sends it to the gin.Engine for routing. // It returns a proxy response object generated from the http.ResponseWriter. func (g *GinLambda) ProxyWithContext(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - ginRequest, err := g.RequestFromEvent(ctx, req) + ginRequest, err := g.EventToRequestWithContext(ctx, req) + return g.proxyInternal(ginRequest, err) +} + +func (g *GinLambda) proxyInternal(req *http.Request, err error) (events.APIGatewayProxyResponse, error) { if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) } respWriter := core.NewProxyResponseWriter() - g.ginEngine.ServeHTTP(http.ResponseWriter(respWriter), ginRequest) + g.ginEngine.ServeHTTP(http.ResponseWriter(respWriter), req) proxyResponse, err := respWriter.GetProxyResponse() if err != nil { diff --git a/gorillamux/adapter.go b/gorillamux/adapter.go index 95f567d..33baf6a 100644 --- a/gorillamux/adapter.go +++ b/gorillamux/adapter.go @@ -23,15 +23,20 @@ func New(router *mux.Router) *GorillaMuxAdapter { // Proxy receives an API Gateway proxy event, transforms it into an http.Request // object, and sends it to the mux.Router for routing. // It returns a proxy response object generated from the http.ResponseWriter. -func (h *GorillaMuxAdapter) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - return h.ProxyWithContext(context.Background(), req) +func (h *GorillaMuxAdapter) Proxy(event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + req, err := h.ProxyEventToHTTPRequest(event) + return h.proxyInternal(req, err) } // ProxyWithContext receives context and an API Gateway proxy event, // transforms them into an http.Request object, and sends it to the mux.Router for routing. // It returns a proxy response object generated from the http.ResponseWriter. func (h *GorillaMuxAdapter) ProxyWithContext(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - req, err := h.RequestFromEvent(ctx, event) + req, err := h.EventToRequestWithContext(ctx, event) + return h.proxyInternal(req, err) +} + +func (h *GorillaMuxAdapter) proxyInternal(req *http.Request, err error) (events.APIGatewayProxyResponse, error) { if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) } diff --git a/handlerfunc/adapter.go b/handlerfunc/adapter.go index 8e70e0a..f4d1eaa 100644 --- a/handlerfunc/adapter.go +++ b/handlerfunc/adapter.go @@ -22,15 +22,20 @@ func New(handlerFunc http.HandlerFunc) *HandlerFuncAdapter { // Proxy receives an API Gateway proxy event, transforms it into an http.Request // object, and sends it to the http.HandlerFunc for routing. // It returns a proxy response object generated from the http.ResponseWriter. -func (h *HandlerFuncAdapter) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - return h.ProxyWithContext(context.Background(), req) +func (h *HandlerFuncAdapter) Proxy(event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + req, err := h.ProxyEventToHTTPRequest(event) + return h.proxyInternal(req, err) } // ProxyWithContext receives context and an API Gateway proxy event, // transforms them into an http.Request object, and sends it to the http.HandlerFunc for routing. // It returns a proxy response object generated from the http.ResponseWriter. func (h *HandlerFuncAdapter) ProxyWithContext(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - req, err := h.RequestFromEvent(ctx, event) + req, err := h.EventToRequestWithContext(ctx, event) + return h.proxyInternal(req, err) +} + +func (h *HandlerFuncAdapter) proxyInternal(req *http.Request, err error) (events.APIGatewayProxyResponse, error) { if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) } diff --git a/httpadapter/adapter.go b/httpadapter/adapter.go index 1b25b10..e27e827 100644 --- a/httpadapter/adapter.go +++ b/httpadapter/adapter.go @@ -22,15 +22,20 @@ func New(handler http.Handler) *HandlerAdapter { // Proxy receives an API Gateway proxy event, transforms it into an http.Request // object, and sends it to the http.HandlerFunc for routing. // It returns a proxy response object generated from the http.Handler. -func (h *HandlerAdapter) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - return h.ProxyWithContext(context.Background(), req) +func (h *HandlerAdapter) Proxy(event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + req, err := h.ProxyEventToHTTPRequest(event) + return h.proxyInternal(req, err) } // ProxyWithContext receives context and an API Gateway proxy event, // transforms them into an http.Request object, and sends it to the http.Handler for routing. // It returns a proxy response object generated from the http.ResponseWriter. func (h *HandlerAdapter) ProxyWithContext(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - req, err := h.RequestFromEvent(ctx, event) + req, err := h.EventToRequestWithContext(ctx, event) + return h.proxyInternal(req, err) +} + +func (h *HandlerAdapter) proxyInternal(req *http.Request, err error) (events.APIGatewayProxyResponse, error) { if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) } diff --git a/negroni/adapter.go b/negroni/adapter.go index 8b065a9..ecafdaf 100644 --- a/negroni/adapter.go +++ b/negroni/adapter.go @@ -23,15 +23,20 @@ func New(n *negroni.Negroni) *NegroniAdapter { // Proxy receives an API Gateway proxy event, transforms it into an http.Request // object, and sends it to the negroni.Negroni for routing. // It returns a proxy response object generated from the http.Handler. -func (h *NegroniAdapter) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - return h.ProxyWithContext(context.Background(), req) +func (h *NegroniAdapter) Proxy(event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { + req, err := h.ProxyEventToHTTPRequest(event) + return h.proxyInternal(req, err) } // ProxyWithContext receives context and an API Gateway proxy event, // transforms them into an http.Request object, and sends it to the negroni.Negroni for routing. // It returns a proxy response object generated from the http.ResponseWriter. func (h *NegroniAdapter) ProxyWithContext(ctx context.Context, event events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { - req, err := h.RequestFromEvent(ctx, event) + req, err := h.EventToRequestWithContext(ctx, event) + return h.proxyInternal(req, err) +} + +func (h *NegroniAdapter) proxyInternal(req *http.Request, err error) (events.APIGatewayProxyResponse, error) { if err != nil { return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) } From efeb9bca8f73c4a39a1869918271a3eeb495d16b Mon Sep 17 00:00:00 2001 From: Nikolay Sarychev Date: Sun, 28 Apr 2019 08:05:50 -0600 Subject: [PATCH 12/12] Rename for consistency --- core/request.go | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/core/request.go b/core/request.go index 6ddc1f3..2ca4032 100644 --- a/core/request.go +++ b/core/request.go @@ -108,29 +108,29 @@ func (r *RequestAccessor) StripBasePath(basePath string) string { // Returns the populated http request with additional two custom headers for the stage variables and API Gateway context. // To access these properties use the GetAPIGatewayStageVars and GetAPIGatewayContext method of the RequestAccessor object. func (r *RequestAccessor) ProxyEventToHTTPRequest(req events.APIGatewayProxyRequest) (*http.Request, error) { - httpRequest, err := r.RequestFromEvent(req) + httpRequest, err := r.EventToRequest(req) if err != nil { log.Println(err) return nil, err } - return AddToHeader(httpRequest, req) + return addToHeader(httpRequest, req) } // EventToRequestWithContext converts an API Gateway proxy event and context into an http.Request object. // Returns the populated http request with lambda context, stage variables and APIGatewayProxyRequestContext as part of its context. // Access those using GetAPIGatewayContextFromContext, GetStageVarsFromContext and GetRuntimeContextFromContext functions in this package. func (r *RequestAccessor) EventToRequestWithContext(ctx context.Context, req events.APIGatewayProxyRequest) (*http.Request, error) { - httpRequest, err := r.RequestFromEvent(req) + httpRequest, err := r.EventToRequest(req) if err != nil { log.Println(err) return nil, err } - return AddToContext(ctx, httpRequest, req), nil + return addToContext(ctx, httpRequest, req), nil } -// RequestFromEvent converts an API Gateway proxy event into an http.Request object. +// EventToRequest converts an API Gateway proxy event into an http.Request object. // Returns the populated request maintaining headers -func (r *RequestAccessor) RequestFromEvent(req events.APIGatewayProxyRequest) (*http.Request, error) { +func (r *RequestAccessor) EventToRequest(req events.APIGatewayProxyRequest) (*http.Request, error) { decodedBody := []byte(req.Body) if req.IsBase64Encoded { base64Body, err := base64.StdEncoding.DecodeString(req.Body) @@ -185,8 +185,7 @@ func (r *RequestAccessor) RequestFromEvent(req events.APIGatewayProxyRequest) (* return httpRequest, nil } -// AddToHeader adds API gateway context and stage variables to headers on http request -func AddToHeader(req *http.Request, apiGwRequest events.APIGatewayProxyRequest) (*http.Request, error) { +func addToHeader(req *http.Request, apiGwRequest events.APIGatewayProxyRequest) (*http.Request, error) { stageVars, err := json.Marshal(apiGwRequest.StageVariables) if err != nil { log.Println("Could not marshal stage variables for custom header") @@ -202,8 +201,7 @@ func AddToHeader(req *http.Request, apiGwRequest events.APIGatewayProxyRequest) return req, nil } -// AddToContext adds context, API gateway context and stage variables to context on http request -func AddToContext(ctx context.Context, req *http.Request, apiGwRequest events.APIGatewayProxyRequest) *http.Request { +func addToContext(ctx context.Context, req *http.Request, apiGwRequest events.APIGatewayProxyRequest) *http.Request { lc, _ := lambdacontext.FromContext(ctx) rc := requestContext{lambdaContext: lc, gatewayProxyContext: apiGwRequest.RequestContext, stageVars: apiGwRequest.StageVariables} ctx = context.WithValue(req.Context(), ctxKey{}, rc)