lambada

package module
v0.0.0-...-f261e62 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Mar 20, 2024 License: MIT Imports: 14 Imported by: 0

README

Lambada - Go net/http compatibility layer for AWS API Gateway Lambda functions

Lambada is a small Go package which provides a layer allowing to use Go's standard library net/http package to handle AWS API Gateway events in AWS Lambda functions.

It basically converts API Gateway events into http.Request, calls an http.Handler and converts the result written to the http.Response into an API Gateway response.

All libraries using http.Handler (e.g. multiplexers) should work using Lambada.

Lambada is compatible with both API Gateway V1 using the Lambda Proxy integration, and API Gateway V2 (HTTP API).

Installation

Install using go get:

go get github.com/rajarathnabalan/lambada

Quick Start

The quickest way to get started with Lambada is simply to use the lambada.Serve function in place of lambda.Start:

import (
    "net/http"
    "github.com/rajarathnabalan/lambada"
)

func main() {
    // Start the Lambda function handler with an http.Handler
    lambada.Serve(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	       w.Write(([]byte)("<html><body><h1>Hello, World!</h1></body></html>"))
    }))
}

If you wish to control how do you start the Lambda handler, use lambada.NewHandler:

import (
    "net/http"
    "github.com/rajarathnabalan/lambada"
    "github.com/aws/aws-lambda-go/lambda"
)

func main() {
    // Create a new handler
    h := lambada.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	       w.Write(([]byte)("<html><body><h1>Hello, World!</h1></body></html>"))
    })

    // Start the Lambda handler
    lambda.Start(h)
}

You can also customize how Lambada will behave by passing some options to lambada.NewHandler or lambada.ServeWithOptions. Available options are described below.

Logging

By default, Lambada does not log anything. It is however possible to log the incoming Lambda events and the Lambda response generated by Lambada.

Three options are available to control logging:

  • lambada.WithRequestLogger - Sets the request logger
  • lambada.WithResponseLogger - Sets the response logger
  • lambada.WithLogger - Sets both loggers

A Logger is a simple interface which is compatible with stdlib's log.Logger:

    lambada.ServeWithOptions(handler, lambada.WithLogger(log.Default()))

Lambada provides a NullLogger type which disables logging (which is used by default):

    lambada.ServeWithOptions(handler, lambada.WithLogger(lambada.NullLogger{}))

Responses with binary content

Returning responses with binary content can be a bit tedious using AWS Lambda and API Gateway, as the body must be base64 encoded.

By default, Lambada will always assume the response body is text only, but there are several options available.

Global option: Enabling binary by default

Using the lambada.WithDefaultBinary option, binary mode can be enabled for all requests:

    lambada.ServeWithOptions(handler, lambada.WithDefaultBinary(true))
Per request setting: Setting text or binary output for a specific request

Inside an HTTP handler, you can use the lambada.SetBinary and lambada.SetText to enable/disable binary for this specific request:

    h := lambada.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Enable binary mode
        lambada.SetBinary(w)

        // Enable text mode
        lambada.SetText(w)
    })

Note that lambada.SetBinary and lambada.SetText have no effect when running the code on a non-Lambda environment.

Using Output Mode

The last but not least option is the output mode. Output mode controls how Lambada will process the request output body. There are three available output modes:

  • lambada.Manual - Nothing will be done. You'll have to call SetBinary or SetText to switch modes.
  • lambada.AutoContentType - This is the default. If your response does not include a Content-Type header, Lambada will sniff the response body (using http.DetectContentType) to set an appropriate Content-Type header. Nothing else will be done, so you'll still have to call SetBinary or SetText as needed.
  • lambada.Automatic - This mode will work as AutoContentType but will in addition enable or disable binary based on the Content-Type and Content-Encoding response headers. If Lambada cannot determine if the body is binary or not, the default value will be used.

The output mode can be set either globally using the lambada.WithOutputMode option or per request using lambada.SetOutputMode:

    h := lambada.NewHandler(
        http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            // Set manual mode for this request specifically
            lambada.SetOutputMode(lambada.Manual)
        },
        // Use the Automatic mode globally
        lambada.WithOutputMode(lambada.Automatic),
    )

Note that lambada.SetOutputMode have no effect when running the code on a non-Lambda environment.

Accessing Lambada internals

Lambada aims to be an abstraction layer over AWS Lambda / API Gateway. However, it may sometimes be useful to access the original Lambda event.

Accessing the Lambda event

The lambada.GetRequest function returns the original Lambda event from a request's context:

    h := lambada.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        // Get original request
        lambdaRequest := lambada.GetRequest(r.Context())
        if lambdaRequest != nil {
            // lambdaRequest contains the original event.
            // Note that the content will vary whether the event was an API Gateway v1 or v2 event.
        } else {
            // lambdaRequest is nil if the handler has not been called from a Lambda function
        }
    })
Accessing the response writer

It is also possible to unwrap to get the underlying lambda.ResponseWriter:

    h := lambada.NewHandler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        lambdaWriter, ok := w.(*lambada.ResponseWriter)
        if ok {
            // Do something with lambdaWriter
        } else {
            // The handler has not been called from a Lambda function
        }
    })

Documentation

Overview

Package lambada provides a compatibility layer allowing to implement AWS API Gateway V1 and V2 (HTTP APIs) Lambda integrations using http.Handler. All libraries and frameworks using net/http should work using lambada.

Example:

package main

import (
    "net/http"

    "github.com/rajarathnabalan/lambada"
)

func main() {
    lambada.Serve(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
	       w.Write(([]byte)("<html><body><h1>Hello, World!</h1></body></html>"))
    }))
}

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func IsLambda

func IsLambda() bool

IsLambda returns whether or not the code is running in a Lambda environment. This is done by checking if the environment variable AWS_LAMBDA_FUNCTION_NAME is set.

func Serve

func Serve(h http.Handler)

Serve starts the Lambda handler using the http.Handler to serve incoming requests. Serve calls lambda.Start(NewHandler(h)) under the hood.

func ServeWithOptions

func ServeWithOptions(h http.Handler, options ...Option)

ServeWithOptions starts the lambda handler using the http.Handler and options to serve incoming requests. ServeWithOptions calls lambda.Start(NewHandler(h, options...)) under the hood.

func SetBinary

func SetBinary(w http.ResponseWriter)

SetBinary enforces binary mode for the given ResponseWriter. That is, the response will be encoded to Base64 when returned to API Gateway.

If the passed ResponseWriter has not been provided by Lambada, this function has no effect.

func SetOutputMode

func SetOutputMode(w http.ResponseWriter, outputMode OutputMode)

SetOutputMode sets the given output mode to the given ResponseWriter.

func SetText

func SetText(w http.ResponseWriter)

SetText enforces text mode for the given ResponseWriter. That is, the response will be be set to not be encoded to Base64 when returned to API Gateway.

If the passed ResponseWriter has not been provided by Lambada, this function has no effect.

func WithRequest

func WithRequest(ctx context.Context, req *Request) context.Context

WithRequest returns a new context.Context with the given Request attached. The returned context should then be attached to an http.Request.

There's usually no need to call this function directly, as all the work is done by lambada itself. However, it may be useful for testing purposes.

Types

type Authorizer

type Authorizer struct {
	IAM *IAMAuthorizer `json:"iam,omitempty"`
	JWT *JWTAuthorizer `json:"jwt,omitempty"`
}

Authorizer contains authorizer details

type IAMAuthorizer

type IAMAuthorizer struct {
	AccessKey      string `json:"accessKey,omitempty"`
	AccountID      string `json:"accountId,omitempty"`
	CallerID       string `json:"callerId,omitempty"`
	PrincipalOrgID string `json:"principalOrgId,omitempty"`
	UserARN        string `json:"userArn,omitempty"`
	UserID         string `json:"userId,omitempty"`
}

IAMAuthorizer contains the details of a request authenticated using the AWS SignV4 authorizer.

type JWTAuthorizer

type JWTAuthorizer struct {
	Claims jwtclaims.Claims `json:"claims,omitempty"`
	Scopes interface{}      `json:"scopes,omitempty"`
}

JWTAuthorizer contains the details of a request authenticated using the JWT authorizer.

type LambadaHandler

type LambadaHandler func(ctx context.Context, req Request) (Response, error)

LambdaHandler is a Lambada lambda handler function which can be used with lambda.Start.

func NewHandler

func NewHandler(h http.Handler, options ...Option) LambadaHandler

NewHandler returns a Lambda function handler which can be used with lambda.Start. The returned lambda handler wraps incoming requets into http.Request, calls the provided http.Handler and converts the response into an API Gateway response.

type Logger

type Logger interface {
	Printf(fmt string, args ...interface{})
}

A Logger interface. Provide a single Printf method, which is compatible with the standard library log package.

type NullLogger

type NullLogger struct{}

NullLogger is a no-op implementation of Logger. All messages sent to it are ignored.

func (NullLogger) Printf

func (l NullLogger) Printf(fmt string, args ...interface{})

type Option

type Option func(*options)

An Option used to customize the handler behavior

func WithDefaultBinary

func WithDefaultBinary(defaultBinary bool) Option

WithDefaultBinary enables or disable the default binary mode.

func WithLogger

func WithLogger(logger Logger) Option

WithLogger sets all the handler's loggers to logger.

func WithOutputMode

func WithOutputMode(outputMode OutputMode) Option

WithOutputMode sets the handler's output mode. OutputMode may be one of Manual, AutoContentType or Automatic.

func WithRequestLogger

func WithRequestLogger(logger Logger) Option

WithRequestLogger sets the handler's request logger.

func WithResponseLogger

func WithResponseLogger(logger Logger) Option

WithResponseLogger sets the handler's response logger.

type OutputMode

type OutputMode int8

OutputMode represents the way the request's output will be handled. See the defined OutputMode cconstant to get details on available output modes and how they work.

const (
	// Fully manual mode. Neither Content-Type or binary mode will be automatically activated on the responses.
	// Binary mode may be manually set on the response writer if needed.
	Manual OutputMode = -1

	// The Content-Type will automatically be set in the response, if not already provided.
	// The binary mode will not be activated automatically and must be set manually when required.
	//
	// This is the default mode for backward compatibility reasons.
	AutoContentType OutputMode = 0

	// Fully automatic mode: The Content-Type will be set in the response, if not already provided.
	// If the Content-Type indicated binary data, binary mode is also activated automatically.
	// Binary mode can still be forcefully manually enabled, but cannot be forced to disabled.
	Automatic OutputMode = 1
)

type Request

type Request struct {
	// V1 Only
	Resource                        string              `json:"resource"` // The resource path defined in API Gateway
	Path                            string              `json:"path"`     // The url path for the caller
	HTTPMethod                      string              `json:"httpMethod"`
	MultiValueHeaders               map[string][]string `json:"multiValueHeaders"`
	MultiValueQueryStringParameters map[string][]string `json:"multiValueQueryStringParameters"`

	// V2 Only
	Version        string   `json:"version"`
	RouteKey       string   `json:"routeKey"`
	RawPath        string   `json:"rawPath"`
	RawQueryString string   `json:"rawQueryString"`
	Cookies        []string `json:"cookies,omitempty"`

	// V1 + V2
	Headers               map[string]string `json:"headers"`
	QueryStringParameters map[string]string `json:"queryStringParameters,omitempty"`
	PathParameters        map[string]string `json:"pathParameters,omitempty"`
	StageVariables        map[string]string `json:"stageVariables,omitempty"`
	Body                  string            `json:"body,omitempty"`
	IsBase64Encoded       bool              `json:"isBase64Encoded,omitempty"`
	RequestContext        RequestContext    `json:"requestContext"`
}

Request represents an API Gateway event. This struct is both compatible with V1 (Lambda Proxy Integration) and V2 (HTTP API) events and is basically a merge of the `APIGatewayProxyRequest` and `APIGatewayV2HTTPRequest` structs defined in the `github.com/aws/aws-lambda-go/events` package.

func GetRequest

func GetRequest(r *http.Request) *Request

GetRequest returns the original API Gateway request which issued the http.Request. The returned Request value contains both API Gateway V1 and V2 data, but the used fields depend on the actual API Gateway version used.

When no API Gateway request is attached to the http.Request, this function returns nil.

type RequestContext

type RequestContext struct {
	// V1 Only
	ResourceID       string                           `json:"resourceId"`
	OperationName    string                           `json:"operationName,omitempty"`
	Protocol         string                           `json:"protocol"`
	Identity         events.APIGatewayRequestIdentity `json:"identity"`
	ResourcePath     string                           `json:"resourcePath"`
	HTTPMethod       string                           `json:"httpMethod"`
	RequestTime      string                           `json:"requestTime"`
	RequestTimeEpoch int64                            `json:"requestTimeEpoch"`

	// V2 Only
	RouteKey  string                                               `json:"routeKey"`
	Time      string                                               `json:"time"`
	TimeEpoch int64                                                `json:"timeEpoch"`
	HTTP      events.APIGatewayV2HTTPRequestContextHTTPDescription `json:"http"`

	// V1 + V2
	AccountID    string      `json:"accountId"`
	Stage        string      `json:"stage"`
	DomainName   string      `json:"domainName"`
	DomainPrefix string      `json:"domainPrefix"`
	RequestID    string      `json:"requestId"`
	APIID        string      `json:"apiId"` // The API Gateway rest API Id
	Authorizer   *Authorizer `json:"authorizer,omitempty"`
}

RequestContext contains the information to identify the AWS account and resources invoking the Lambda function. This struct is both compatible with V1 (Lambda Proxy Integration) and V2 (HTTP API) events and is basically a merge of the `APIGatewayProxyRequestContext` and `APIGatewayV2HTTPRequestContext` structs defined in the `github.com/aws/aws-lambda-go/events` package.

type Response

type Response struct {
	// V2 Only
	Cookies []string `json:"cookies,omitempty"`

	// V1 + V2
	StatusCode        int                 `json:"statusCode"`
	Headers           map[string]string   `json:"headers"`
	MultiValueHeaders map[string][]string `json:"multiValueHeaders"`
	Body              string              `json:"body"`
	IsBase64Encoded   bool                `json:"isBase64Encoded,omitempty"`
}

Response contains the response to send back to API Gateway This struct is both compatible with V1 (Lambda Proxy Integration) and V2 (HTTP API) events and is basically a merge of the `APIGatewayProxyResponse` and `APIGatewayV2HTTPResponse` structs defined in the `github.com/aws/aws-lambda-go/events` package.

type ResponseWriter

type ResponseWriter struct {
	// contains filtered or unexported fields
}

ResponseWriter is an implementation of http.ResponseWriter which stores the data written to it internally. Trailers are not supported by this implementation.

You usually access ResponseWriter through the http.ResponseWriter interface. If you need to access the underlying ResponseWriter use:

// w is an http.ResponseWriter
rw, ok := w.(*lambada.ResponseWriter)

func (*ResponseWriter) AllowBinaryDetection

func (w *ResponseWriter) AllowBinaryDetection()

AllowBinaryDetection allows binary detection to happen on w. Automatic binary detection will only happen when the OutputMode is set to Automatic.

func (*ResponseWriter) Body

func (w *ResponseWriter) Body() []byte

Body returns the current body's byte. If nothing has been written, Body returns nil. The returned slice is valid until the next call to Write.

func (*ResponseWriter) Header

func (w *ResponseWriter) Header() http.Header

Header returns the response's header set. Once the WriteHeader has been called, Header returns a new copy of the response's headers, preventing them from being modified.

func (*ResponseWriter) SetBinary

func (w *ResponseWriter) SetBinary(binary bool)

SetBinary sets whether or not the binary mode should be enabled or not. When binary mode is enabled, the response is encoded to Base64 before being returned to API Gateway. The mode set through this function is forced - meaning that automatic binary detection will be skipped. Use AllowBinaryDetection() to revert this behavior.

func (*ResponseWriter) SetOutputMode

func (w *ResponseWriter) SetOutputMode(outputMode OutputMode)

SetOutputMode sets the output mode for w.

func (*ResponseWriter) StatusCode

func (w *ResponseWriter) StatusCode() int

StatusCode returns w's current status code. If WriteHeaders() has not been called yet, returns 200.

func (*ResponseWriter) Write

func (w *ResponseWriter) Write(data []byte) (int, error)

func (*ResponseWriter) WriteHeader

func (w *ResponseWriter) WriteHeader(statusCode int)

Directories

Path Synopsis
Package jwtclaims provide the Claims type providing helper functions to work with JWT claims
Package jwtclaims provide the Claims type providing helper functions to work with JWT claims

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL