|
| 1 | +// Package fiberadapter adds Fiber support for the aws-severless-go-api library. |
| 2 | +// Uses the core package behind the scenes and exposes the New method to |
| 3 | +// get a new instance and Proxy method to send request to the Fiber app. |
| 4 | +package fiberadapter |
| 5 | + |
| 6 | +import ( |
| 7 | + "context" |
| 8 | + "io/ioutil" |
| 9 | + "net" |
| 10 | + "net/http" |
| 11 | + |
| 12 | + "github.com/aws/aws-lambda-go/events" |
| 13 | + "github.com/awslabs/aws-lambda-go-api-proxy/core" |
| 14 | + "github.com/gofiber/fiber/v2" |
| 15 | + "github.com/gofiber/fiber/v2/utils" |
| 16 | + "github.com/valyala/fasthttp" |
| 17 | +) |
| 18 | + |
| 19 | +// FiberLambda makes it easy to send API Gateway proxy events to a fiber.App. |
| 20 | +// The library transforms the proxy event into an HTTP request and then |
| 21 | +// creates a proxy response object from the *fiber.Ctx |
| 22 | +type FiberLambda struct { |
| 23 | + core.RequestAccessor |
| 24 | + app *fiber.App |
| 25 | +} |
| 26 | + |
| 27 | +// New creates a new instance of the FiberLambda object. |
| 28 | +// Receives an initialized *fiber.App object - normally created with fiber.New(). |
| 29 | +// It returns the initialized instance of the FiberLambda object. |
| 30 | +func New(app *fiber.App) *FiberLambda { |
| 31 | + return &FiberLambda{ |
| 32 | + app: app, |
| 33 | + } |
| 34 | +} |
| 35 | + |
| 36 | +// Proxy receives an API Gateway proxy event, transforms it into an http.Request |
| 37 | +// object, and sends it to the fiber.App for routing. |
| 38 | +// It returns a proxy response object generated from the http.ResponseWriter. |
| 39 | +func (f *FiberLambda) Proxy(req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { |
| 40 | + fiberRequest, err := f.ProxyEventToHTTPRequest(req) |
| 41 | + return f.proxyInternal(fiberRequest, err) |
| 42 | +} |
| 43 | + |
| 44 | +// ProxyWithContext receives context and an API Gateway proxy event, |
| 45 | +// transforms them into an http.Request object, and sends it to the echo.Echo for routing. |
| 46 | +// It returns a proxy response object generated from the http.ResponseWriter. |
| 47 | +func (f *FiberLambda) ProxyWithContext(ctx context.Context, req events.APIGatewayProxyRequest) (events.APIGatewayProxyResponse, error) { |
| 48 | + fiberRequest, err := f.EventToRequestWithContext(ctx, req) |
| 49 | + return f.proxyInternal(fiberRequest, err) |
| 50 | +} |
| 51 | + |
| 52 | +func (f *FiberLambda) proxyInternal(req *http.Request, err error) (events.APIGatewayProxyResponse, error) { |
| 53 | + |
| 54 | + if err != nil { |
| 55 | + return core.GatewayTimeout(), core.NewLoggedError("Could not convert proxy event to request: %v", err) |
| 56 | + } |
| 57 | + |
| 58 | + resp := core.NewProxyResponseWriter() |
| 59 | + f.adaptor(resp, req) |
| 60 | + |
| 61 | + proxyResponse, err := resp.GetProxyResponse() |
| 62 | + if err != nil { |
| 63 | + return core.GatewayTimeout(), core.NewLoggedError("Error while generating proxy response: %v", err) |
| 64 | + } |
| 65 | + |
| 66 | + return proxyResponse, nil |
| 67 | +} |
| 68 | + |
| 69 | +func (f *FiberLambda) adaptor(w http.ResponseWriter, r *http.Request) { |
| 70 | + // New fasthttp request |
| 71 | + req := fasthttp.AcquireRequest() |
| 72 | + defer fasthttp.ReleaseRequest(req) |
| 73 | + |
| 74 | + // Convert net/http -> fasthttp request |
| 75 | + body, err := ioutil.ReadAll(r.Body) |
| 76 | + if err != nil { |
| 77 | + http.Error(w, utils.StatusMessage(fiber.StatusInternalServerError), fiber.StatusInternalServerError) |
| 78 | + return |
| 79 | + } |
| 80 | + req.Header.SetContentLength(len(body)) |
| 81 | + _, _ = req.BodyWriter().Write(body) |
| 82 | + |
| 83 | + req.Header.SetMethod(r.Method) |
| 84 | + req.SetRequestURI(r.RequestURI) |
| 85 | + req.SetHost(r.Host) |
| 86 | + for key, val := range r.Header { |
| 87 | + for _, v := range val { |
| 88 | + req.Header.Add(key, v) |
| 89 | + } |
| 90 | + } |
| 91 | + |
| 92 | + remoteAddr, err := net.ResolveTCPAddr("tcp", r.RemoteAddr) |
| 93 | + if err != nil { |
| 94 | + http.Error(w, utils.StatusMessage(fiber.StatusInternalServerError), fiber.StatusInternalServerError) |
| 95 | + return |
| 96 | + } |
| 97 | + |
| 98 | + // New fasthttp Ctx |
| 99 | + var fctx fasthttp.RequestCtx |
| 100 | + fctx.Init(req, remoteAddr, nil) |
| 101 | + |
| 102 | + // Pass RequestCtx to Fiber router |
| 103 | + f.app.Handler()(&fctx) |
| 104 | + |
| 105 | + // Set response headers |
| 106 | + fctx.Response.Header.VisitAll(func(k, v []byte) { |
| 107 | + w.Header().Set(utils.UnsafeString(k), utils.UnsafeString(v)) |
| 108 | + }) |
| 109 | + |
| 110 | + // Set response statuscode |
| 111 | + w.WriteHeader(fctx.Response.StatusCode()) |
| 112 | + |
| 113 | + // Set response body |
| 114 | + _, _ = w.Write(fctx.Response.Body()) |
| 115 | +} |
0 commit comments